/* * for the TEA6300 chip (only found on Gateway STB TV/FM cards tho the best * of my knowledge) * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF THE WRONG * CHIP (i.e., an MSP3400) IS ON I2C ADDRESS 0x80 (it relies on i2c to * make sure that there is a device acknowledging that address). This * is a potential problem because the MSP3400 is very popular and does * use this address! You have been warned! * * Copyright (c) 1998 Greg Alexander * This code is placed under the terms of the GNU General Public License * Code liberally copied from msp3400.c, which is by Gerd Knorr * * All of this should work, though it would be nice to eventually support * balance (different left,right values) and, if someone ever finds a card * with the support (or if you're careful with a soldering iron), fade * (front/back). */ #include #include #include #include #include #include #include #include #include "i2c.h" #include "bttv.h" #include "videodev.h" #include "audiochip.h" static int debug = 0; /* insmod parameter */ struct tea6300 { struct i2c_bus *bus; int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ int stereo; __u16 left,right; __u16 bass,treble; }; #define dprintk if (debug) printk /* I don't understand this part (I haven't investigated), it's just there * because that's how MSP3400 did it. */ #if LINUX_VERSION_CODE >= 0x020117 MODULE_PARM(debug,"i"); #endif #define I2C_TEA6300 0x80 /* registers (subaddresses): */ #define TEA6300_VL 0x00 /* volume left */ #define TEA6300_VR 0x01 /* volume right */ #define TEA6300_BA 0x02 /* bass */ #define TEA6300_TR 0x03 /* treble */ #define TEA6300_FA 0x04 /* fader control */ #define TEA6300_S 0x05 /* switch register */ /* values for those registers: */ #define TEA6300_S_SA 0x01 /* stereo A input */ #define TEA6300_S_SB 0x02 /* stereo B */ #define TEA6300_S_SC 0x04 /* stereo C */ #define TEA6300_S_GMU 0x80 /* general mute */ /* ******************************** * * functions for talking to TEA6300 * * ******************************** */ static int tea6300_write(struct i2c_bus *bus, int addr, int val) { int ret = 0; i2c_start(bus); if (i2c_sendbyte(bus, I2C_TEA6300, 0) || i2c_sendbyte(bus, addr, 0) || i2c_sendbyte(bus, val, 0)) ret = -1; i2c_stop(bus); if (-1 == ret) { printk(KERN_WARNING "tea6300: I/O error, trying (write %d 0x%x)\n", addr, val); } return ret; } static void tea6300_set(struct tea6300 *tea) { /* mode is ignored today */ dprintk(KERN_DEBUG "tea6300_set(%04x,%04x,%04x,%04x)\n",tea->left>>10,tea->right>>10,tea->bass>>12,tea->treble>>12); tea6300_write(tea->bus, TEA6300_VL, tea->left>>10 ); tea6300_write(tea->bus, TEA6300_VR, tea->right>>10 ); tea6300_write(tea->bus, TEA6300_BA, tea->bass>>12 ); tea6300_write(tea->bus, TEA6300_TR, tea->treble>>12); } static void tea6300_init(struct tea6300 *tea) { tea->left=tea->right =49152; /* -10dB (loud enough, but not beyond normal line levels - so as to avoid clipping */ tea->bass=tea->treble=28672; /* 0dB */ tea->mode=AUDIO_OFF; tea->stereo=1; /* left=right=0x27<<10, bass=treble=0x07<<12 */ tea6300_write(tea->bus, TEA6300_FA, 0x3f ); /* fader off */ tea6300_write(tea->bus, TEA6300_S , TEA6300_S_GMU); /* mute */ tea6300_set(tea); } static void tea6300_audio(struct tea6300 *tea, int mode) { /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ dprintk(KERN_DEBUG "tea6300_audio:%d (T,R,E,I,O)\n",mode); tea->mode=mode; if (mode==AUDIO_OFF) { /* just mute it */ tea6300_write(tea->bus, TEA6300_S, TEA6300_S_GMU); return; } switch(mode) { case AUDIO_TUNER: tea6300_write(tea->bus, TEA6300_S, TEA6300_S_SA); break; case AUDIO_RADIO: tea6300_write(tea->bus, TEA6300_S, TEA6300_S_SB); break; case AUDIO_EXTERN: tea6300_write(tea->bus, TEA6300_S, TEA6300_S_SC); break; } } /* *********************** * * i2c interface functions * * *********************** */ static int tea6300_attach(struct i2c_device *device) { struct tea6300 *tea; LOCK_FLAGS; device->data = tea = kmalloc(sizeof *tea,GFP_KERNEL); if (!tea) return -ENOMEM; memset(tea,0,sizeof *tea); tea->bus = device->bus; LOCK_I2C_BUS(tea->bus); tea6300_init(tea); UNLOCK_I2C_BUS(tea->bus); MOD_INC_USE_COUNT; strcpy(device->name,"TEA6300T"); printk(KERN_INFO "tea6300: initialized\n"); return 0; } static int tea6300_detach(struct i2c_device *device) { struct tea6300 *tea = (struct tea6300 *)device->data; LOCK_FLAGS; LOCK_I2C_BUS(tea->bus); tea6300_init(tea); UNLOCK_I2C_BUS(tea->bus); kfree(tea); MOD_DEC_USE_COUNT; return 0; } static int tea6300_command(struct i2c_device *device, unsigned int cmd, void *arg) { struct tea6300 *tea = (struct tea6300 *)device->data; __u16 *sarg = arg; LOCK_FLAGS; switch (cmd) { case AUDC_SET_RADIO: LOCK_I2C_BUS(tea->bus); tea6300_audio(tea,AUDIO_RADIO); UNLOCK_I2C_BUS(tea->bus); break; case AUDC_GET_VOLUME_LEFT: *sarg = tea->left; break; case AUDC_GET_VOLUME_RIGHT: *sarg = tea->right; break; case AUDC_SET_VOLUME_LEFT: tea->left = *sarg; LOCK_I2C_BUS(tea->bus); tea6300_set(tea); UNLOCK_I2C_BUS(tea->bus); break; case AUDC_SET_VOLUME_RIGHT: tea->right = *sarg; LOCK_I2C_BUS(tea->bus); tea6300_set(tea); UNLOCK_I2C_BUS(tea->bus); break; case AUDC_GET_BASS: *sarg = tea->bass; break; case AUDC_SET_BASS: tea->bass = *sarg; LOCK_I2C_BUS(tea->bus); tea6300_set(tea); UNLOCK_I2C_BUS(tea->bus); break; case AUDC_GET_TREBLE: *sarg = tea->treble; break; case AUDC_SET_TREBLE: tea->treble = *sarg; LOCK_I2C_BUS(tea->bus); tea6300_set(tea); UNLOCK_I2C_BUS(tea->bus); break; case AUDC_GET_STEREO: *sarg = tea->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; break; case AUDC_SET_STEREO: tea->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; /* TODO: make this write to the TDA9850? */ break; case AUDC_SET_INPUT: LOCK_I2C_BUS(tea->bus); tea6300_audio(tea,*sarg); UNLOCK_I2C_BUS(tea->bus); break; /* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to case AUDC_NEWCHANNEL: it and it would require preserving state case AUDC_GET_DC: huh?? (not used by bttv.c) */ default: return -EINVAL; } return 0; } static struct i2c_driver i2c_driver_tea = { "tea6300", /* name */ I2C_DRIVERID_AUDIOCHIP, /* ID */ I2C_TEA6300, I2C_TEA6300, /* addr range */ tea6300_attach, tea6300_detach, tea6300_command }; #ifdef MODULE int init_module(void) #else int tea6300_init(void) #endif { i2c_register_driver(&i2c_driver_tea); return 0; } #ifdef MODULE void cleanup_module(void) { i2c_unregister_driver(&i2c_driver_tea); } #endif