/* Driver for the TDA7313 and PT2313L audio processor chips * * Copyright (c) 2003-2006 Mark McClelland * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. */ #define DRIVER_VERSION "1.00" #include #include #include #include #include #include #include #include #include "id.h" #include "audiochip.h" #include "compat.h" #define I2C_TDA7313 0x88 /* Addresses to scan */ static unsigned short normal_i2c[] = {I2C_TDA7313 >> 1, I2C_CLIENT_END}; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; #endif I2C_CLIENT_INSMOD; MODULE_DESCRIPTION("Driver for the TDA7313 and PT2313L audio processor chips"); MODULE_AUTHOR("Mark McClelland "); MODULE_LICENSE("GPL"); struct tda7313 { struct i2c_client c; unsigned char volume; /* Range: 0 - 0x3f */ }; static struct i2c_driver driver; static struct i2c_client client_template; /* ----------------------------------------------------------------------- */ static int tda7313_write(struct i2c_client *c, unsigned char val) { int rc; rc = i2c_smbus_write_byte(c, val); if (rc < 0) printk(KERN_ERR "tda7313: I2C write error\n"); return rc; } static int tda7313_set_volume(struct i2c_client *c, unsigned char val) { struct tda7313 *t = i2c_get_clientdata(c); tda7313_write(c, (~val)&0x3f); t->volume = val; return 0; } static void tda7313_set_defaults(struct i2c_client *c) { tda7313_set_volume(c, 0); /* Disable attenuation */ tda7313_write(c, 0x80); tda7313_write(c, 0xa0); tda7313_write(c, 0xc0); tda7313_write(c, 0xe0); /* Select input 1, loudness enabled (+0dB) */ tda7313_write(c, 0x40); /* Flat EQ */ tda7313_write(c, 0x6f); tda7313_write(c, 0x7f); } /* ----------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68) static int tda7313_attach(struct i2c_adapter *adap, int addr, int kind) #else static int tda7313_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) #endif { struct tda7313 *t; struct i2c_client *c; t = kmalloc(sizeof(*t), GFP_KERNEL); if (!t) return -ENOMEM; memset(t, 0, sizeof(*t)); c = &t->c; memcpy(c, &client_template, sizeof *c); c->adapter = adap; c->addr = addr; i2c_set_clientdata(c, t); tda7313_set_defaults(c); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MOD_INC_USE_COUNT; #endif i2c_attach_client(c); return 0; } static int tda7313_detach(struct i2c_client *c) { struct tda7313 *t = i2c_get_clientdata(c); i2c_detach_client(c); kfree(t); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MOD_DEC_USE_COUNT; #endif return 0; } static int tda7313_probe(struct i2c_adapter *adap) { switch (adap->id) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV511): #else case (I2C_HW_SMBUS_OV511): #endif printk(KERN_INFO "tda7313: probing %s i2c adapter [id=0x%06x]\n", adap->name, adap->id); return i2c_probe(adap, &addr_data, tda7313_attach); default: printk(KERN_INFO "tda7313: ignoring adapter: %s [0x%06x]\n", adap->name, adap->id); return 0; } } static int tda7313_command(struct i2c_client *c, unsigned int cmd, void *arg) { struct tda7313 *t = i2c_get_clientdata(c); switch (cmd) { case VIDIOCGAUDIO: { struct video_audio *va = arg; printk(KERN_DEBUG "tda7313: VIDIOCGAUDIO\n"); va->flags |= VIDEO_AUDIO_VOLUME; va->volume = t->volume*(65535/0x3f); va->balance = 32768; va->bass = 0x8; va->treble = 0x8; va->mode |= VIDEO_SOUND_MONO; return 0; } case VIDIOCSAUDIO: { struct video_audio *va = arg; printk(KERN_DEBUG "tda7313: VIDIOCSAUDIO\n"); tda7313_set_volume(c, va->volume/(65535/0x3f)); return 0; } default: return -ENOIOCTLCMD; } } /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) .owner = THIS_MODULE, # endif .name = "i2c tda7313 driver", #else .driver = { .name = "i2c tda7313 driver", }, #endif .id = I2C_DRIVERID_TDA7313, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7) .class = I2C_CLASS_TV_ANALOG, /* FIXME: = I2C_CLASS_SOUND? */ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) .flags = I2C_DF_NOTIFY, #endif .attach_adapter = tda7313_probe, .detach_client = tda7313_detach, .command = tda7313_command, }; static struct i2c_client client_template = { .name = "tda7313", #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) .id = -1, #endif .driver = &driver, }; static int __init tda7313_init(void) { return i2c_add_driver(&driver); } static void __exit tda7313_exit(void) { i2c_del_driver(&driver); } module_init(tda7313_init); module_exit(tda7313_exit);