/* * for the TDA8425 chip (I don't know which cards have this) * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF A DIFFERENT * CHIP IS AT ADDRESS 0x82 (it relies on i2c to make sure that there is a * device acknowledging that address) * * 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). Also, the chip seems (?) to have * two stereo inputs, so if someone has this card, could they tell me if the * second one can be used for anything (i.e., does it have an external input * that you can't hear even if you set input to composite?) */ #include #include #include #include #include #include #include #include #include #include #include #include "bttv.h" #include "audiochip.h" /* Addresses to scan */ static unsigned short normal_i2c[] = { I2C_TDA8425 >> 1, I2C_CLIENT_END}; static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { normal_i2c, normal_i2c_range, probe, probe_range, ignore, ignore_range, force }; MODULE_PARM(debug,"i"); static int debug = 0; /* insmod parameter */ #define dprintk if (debug) printk struct tda8425 { int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ int stereo; __u16 left,right; __u16 bass,treble; }; static struct i2c_driver driver; static struct i2c_client client_template; #define TDA8425_VL 0x00 /* volume left */ #define TDA8425_VR 0x01 /* volume right */ #define TDA8425_BA 0x02 /* bass */ #define TDA8425_TR 0x03 /* treble */ #define TDA8425_S1 0x08 /* switch functions */ /* values for those registers: */ #define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ #define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */ /* ******************************** * * functions for talking to TDA8425 * * ******************************** */ static int tda8425_write(struct i2c_client *client, int addr, int val) { unsigned char buffer[2]; buffer[0] = addr; buffer[1] = val; if (2 != i2c_master_send(client,buffer,2)) { printk(KERN_WARNING "tda8425: I/O error, trying (write %d 0x%x)\n", addr, val); return -1; } return 0; } static void tda8425_set(struct i2c_client *client) { struct tda8425 *tda = client->data; /* mode is ignored today */ dprintk(KERN_DEBUG "tda8425_set(%04x,%04x,%04x,%04x)\n",tda->left>>10,tda->right>>10,tda->bass>>12,tda->treble>>12); tda8425_write(client, TDA8425_VL, tda->left>>10 |0xC0); tda8425_write(client, TDA8425_VR, tda->right>>10 |0xC0); tda8425_write(client, TDA8425_BA, tda->bass>>12 |0xF0); tda8425_write(client, TDA8425_TR, tda->treble>>12|0xF0); } static void do_tda8425_init(struct i2c_client *client) { struct tda8425 *tda = client->data; tda->left=tda->right =61440; /* 0dB */ tda->bass=tda->treble=24576; /* 0dB */ tda->mode=AUDIO_OFF; tda->stereo=1; /* left=right=0x27<<10, bass=treble=0x07<<12 */ tda8425_write(client, TDA8425_S1, TDA8425_S1_OFF); /* mute */ tda8425_set(client); } static void tda8425_audio(struct i2c_client *client, int mode) { struct tda8425 *tda = client->data; /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ dprintk(KERN_DEBUG "tda8425_audio:%d (T,R,E,I,O)\n",mode); tda->mode=mode; tda8425_write(client, TDA8425_S1, (mode==AUDIO_OFF)?TDA8425_S1_OFF:TDA8425_S1_ON); /* this is the function we'll need to change if it turns out the * input-selecting capabilities should be used. */ } /* *********************** * * i2c interface functions * * *********************** */ static int tda8425_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) { struct tda8425 *tda; struct i2c_client *client; client = kmalloc(sizeof *client,GFP_KERNEL); if (!client) return -ENOMEM; memcpy(client,&client_template,sizeof(struct i2c_client)); client->adapter = adap; client->addr = addr; client->data = tda = kmalloc(sizeof *tda,GFP_KERNEL); if (!tda) return -ENOMEM; memset(tda,0,sizeof *tda); do_tda8425_init(client); MOD_INC_USE_COUNT; strcpy(client->name,"TDA8425"); printk(KERN_INFO "tda8425: init\n"); i2c_attach_client(client); return 0; } static int tda8425_probe(struct i2c_adapter *adap) { if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) return i2c_probe(adap, &addr_data, tda8425_attach); return 0; } static int tda8425_detach(struct i2c_client *client) { struct tda8425 *tda = client->data; do_tda8425_init(client); i2c_detach_client(client); kfree(tda); kfree(client); MOD_DEC_USE_COUNT; return 0; } static int tda8425_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tda8425 *tda = client->data; __u16 *sarg = arg; switch (cmd) { case AUDC_SET_RADIO: tda8425_audio(client,AUDIO_RADIO); break; case AUDC_SET_INPUT: tda8425_audio(client,*sarg); break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ case VIDIOCGAUDIO: { struct video_audio *va = arg; va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; va->volume=MAX(tda->left,tda->right); va->balance=(32768*MIN(tda->left,tda->right))/ (va->volume ? va->volume : 1); va->balance=(tda->leftright)? (65535-va->balance) : va->balance; va->bass = tda->bass; va->treble = tda->treble; break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; tda->left = (MIN(65536 - va->balance,32768) * va->volume) / 32768; tda->right = (MIN(va->balance,32768) * va->volume) / 32768; tda->bass = va->bass; tda->treble = va->treble; tda8425_set(client); break; } #if 0 /* --- old, obsolete interface --- */ case AUDC_GET_VOLUME_LEFT: *sarg = tda->left; break; case AUDC_GET_VOLUME_RIGHT: *sarg = tda->right; break; case AUDC_SET_VOLUME_LEFT: tda->left = *sarg; tda8425_set(client); break; case AUDC_SET_VOLUME_RIGHT: tda->right = *sarg; tda8425_set(client); break; case AUDC_GET_BASS: *sarg = tda->bass; break; case AUDC_SET_BASS: tda->bass = *sarg; tda8425_set(client); break; case AUDC_GET_TREBLE: *sarg = tda->treble; break; case AUDC_SET_TREBLE: tda->treble = *sarg; tda8425_set(client); break; case AUDC_GET_STEREO: *sarg = tda->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; break; case AUDC_SET_STEREO: tda->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; /* TODO: make this write to the TDA9850? */ 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) */ #endif default: /* nothing */ } return 0; } static struct i2c_driver driver = { "i2c tda8424 driver", I2C_DRIVERID_TDA8425, I2C_DF_NOTIFY, tda8425_probe, tda8425_detach, tda8425_command, }; static struct i2c_client client_template = { "(unset)", /* name */ -1, 0, 0, NULL, &driver }; #ifdef MODULE int init_module(void) #else int tda8425_init(void) #endif { i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { i2c_del_driver(&driver); } #endif /* * Local variables: * c-basic-offset: 8 * End: */