*
* Copyright (c) 2000 Gerd Knorr
* based on code by:
- * Eric Sandeen (eric_sandeen@bigfoot.com)
+ * Eric Sandeen (eric_sandeen@bigfoot.com)
* Steve VanDeBogart (vandebo@uclink.berkeley.edu)
* Greg Alexander (galexand@acm.org)
*
* This code is placed under the terms of the GNU General Public License
- *
+ *
* OPTIONS:
* debug - set to 1 if you'd like to see debug messages
*
#include <linux/config.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
/* ---------------------------------------------------------------------- */
/* insmod args */
-MODULE_PARM(debug,"i");
static int debug = 0; /* insmod parameter */
+module_param(debug, int, 0644);
MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips");
MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr");
i2c_clientname(&chip->c));
return -1;
}
- dprintk("%s: chip_read: 0x%x\n",i2c_clientname(&chip->c),buffer);
+ dprintk("%s: chip_read: 0x%x\n",i2c_clientname(&chip->c),buffer);
return buffer;
}
return -1;
}
dprintk("%s: chip_read2: reg%d=0x%x\n",
- i2c_clientname(&chip->c),subaddr,read[0]);
+ i2c_clientname(&chip->c),subaddr,read[0]);
return read[0];
}
static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
{
int i;
-
+
if (0 == cmd->count)
return 0;
DECLARE_WAITQUEUE(wait, current);
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chiplist + chip->type;
-
+
daemonize("%s",i2c_clientname(&chip->c));
allow_signal(SIGTERM);
dprintk("%s: thread started\n", i2c_clientname(&chip->c));
/* don't do anything for radio or if mode != auto */
if (chip->norm == VIDEO_MODE_RADIO || chip->mode != 0)
continue;
-
+
/* have a look what's going on */
desc->checkmode(chip);
-
+
/* schedule next check */
mod_timer(&chip->wt, jiffies+2*HZ);
}
static int tda9840_getmode(struct CHIPSTATE *chip)
{
int val, mode;
-
+
val = chip_read(chip);
mode = VIDEO_SOUND_MONO;
if (val & TDA9840_DS_DUAL)
mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
if (val & TDA9840_ST_STEREO)
mode |= VIDEO_SOUND_STEREO;
-
+
dprintk ("tda9840_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
{
int update = 1;
int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
-
+
switch (mode) {
case VIDEO_SOUND_MONO:
t |= TDA9840_MONO;
* in 1dB steps - mute is 0x27 */
-/* 0x02 - BA in TDA9855 */
+/* 0x02 - BA in TDA9855 */
/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
* in .5dB steps - 0 is 0x0E */
/* Unique to TDA9855: */
/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
* in 3dB steps - mute is 0x0 */
-
+
/* Unique to TDA9850: */
/* lower 4 bits control stereo noise threshold, over which stereo turns off
* set to values of 0x00 through 0x0f for Ster1 through Ster16 */
#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
/* Bits 0 to 3 select various combinations
- * of line in and line out, only the
+ * of line in and line out, only the
* interesting ones are defined */
#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
{
int mode;
- mode = ((TDA985x_STP | TDA985x_SAPP) &
+ mode = ((TDA985x_STP | TDA985x_SAPP) &
chip_read(chip)) >> 4;
/* Add mono mode regardless of SAP and stereo */
/* Allows forced mono */
{
int update = 1;
int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
-
+
switch (mode) {
case VIDEO_SOUND_MONO:
c6 |= TDA985x_MONO;
#define TDA9873_AD 0x01 /* Adjust */
#define TDA9873_PT 0x02 /* Port */
-/* Subaddress 0x00: Switching Data
+/* Subaddress 0x00: Switching Data
* B7..B0:
*
* B1, B0: Input source selection
#define TDA9873_EXT_MONO 1
/* B3, B2: output signal select
- * B4 : transmission mode
+ * B4 : transmission mode
* 0, 0, 1 Mono
* 1, 0, 0 Stereo
- * 1, 1, 1 Stereo (reversed channel)
+ * 1, 1, 1 Stereo (reversed channel)
* 0, 0, 0 Dual AB
* 0, 0, 1 Dual AA
* 0, 1, 0 Dual BB
#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */
-/* Bits C6..C4 control FM stantard
+/* Bits C6..C4 control FM stantard
* C6, C5, C4
* 0, 0, 0 B/G (PAL FM)
* 0, 0, 1 M
#define TDA9873_IDR_FAST 1 << 7
-/* Subaddress 0x02: Port data */
+/* Subaddress 0x02: Port data */
/* E1, E0 free programmable ports P1/P2
0, 0 both ports low
*/
#define TDA9873_MOUT_MONO 0
#define TDA9873_MOUT_FMONO 0
-#define TDA9873_MOUT_DUALA 0
-#define TDA9873_MOUT_DUALB 1 << 3
-#define TDA9873_MOUT_ST 1 << 4
+#define TDA9873_MOUT_DUALA 0
+#define TDA9873_MOUT_DUALB 1 << 3
+#define TDA9873_MOUT_ST 1 << 4
#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3)
-#define TDA9873_MOUT_EXTL 1 << 5
+#define TDA9873_MOUT_EXTL 1 << 5
#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3)
#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4)
#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3)
dprintk("tda9873_setmode(): external input\n");
return;
}
-
+
dprintk("tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
dprintk("tda9873_setmode(): sw_data = %d\n", sw_data);
switch (mode) {
case VIDEO_SOUND_MONO:
- sw_data |= TDA9873_TR_MONO;
+ sw_data |= TDA9873_TR_MONO;
break;
case VIDEO_SOUND_STEREO:
sw_data |= TDA9873_TR_STEREO;
static unsigned int tda9874a_SIF = UNSET;
static unsigned int tda9874a_AMSEL = UNSET;
static unsigned int tda9874a_STD = UNSET;
-MODULE_PARM(tda9874a_SIF,"i");
-MODULE_PARM(tda9874a_AMSEL,"i");
-MODULE_PARM(tda9874a_STD,"i");
+module_param(tda9874a_SIF, int, 0444);
+module_param(tda9874a_AMSEL, int, 0444);
+module_param(tda9874a_STD, int, 0444);
/*
* initialization table for tda9874 decoder:
if(nsr & 0x02) /* NSR.S/MB=1 */
mode |= VIDEO_SOUND_STEREO;
#endif
- if(nsr & 0x01) /* NSR.D/SB=1 */
+ if(nsr & 0x01) /* NSR.D/SB=1 */
mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
} else {
if(dsr & 0x02) /* DSR.IDSTE=1 */
#define TEA6300_S_SC 0x04 /* stereo C */
#define TEA6300_S_GMU 0x80 /* general mute */
+#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */
+#define TEA6320_FFR 0x01 /* fader front right (0-5) */
+#define TEA6320_FFL 0x02 /* fader front left (0-5) */
+#define TEA6320_FRR 0x03 /* fader rear right (0-5) */
+#define TEA6320_FRL 0x04 /* fader rear left (0-5) */
+#define TEA6320_BA 0x05 /* bass (0-4) */
+#define TEA6320_TR 0x06 /* treble (0-4) */
+#define TEA6320_S 0x07 /* switch register */
+ /* values for those registers: */
+#define TEA6320_S_SA 0x07 /* stereo A input */
+#define TEA6320_S_SB 0x06 /* stereo B */
+#define TEA6320_S_SC 0x05 /* stereo C */
+#define TEA6320_S_SD 0x04 /* stereo D */
+#define TEA6320_S_GMU 0x80 /* general mute */
+
#define TEA6420_S_SA 0x00 /* stereo A input */
#define TEA6420_S_SB 0x01 /* stereo B */
#define TEA6420_S_SC 0x02 /* stereo C */
static int tea6300_shift10(int val) { return val >> 10; }
static int tea6300_shift12(int val) { return val >> 12; }
+/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */
+/* 0x0c mirror those immediately higher) */
+static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; }
+static int tea6320_shift11(int val) { return val >> 11; }
+static int tea6320_initialize(struct CHIPSTATE * chip)
+{
+ chip_write(chip, TEA6320_FFR, 0x3f);
+ chip_write(chip, TEA6320_FFL, 0x3f);
+ chip_write(chip, TEA6320_FRR, 0x3f);
+ chip_write(chip, TEA6320_FRL, 0x3f);
+
+ return 0;
+}
+
/* ---------------------------------------------------------------------- */
/* audio chip descriptions - defines+functions for tda8425 */
static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
{
int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
-
+
if (mode & VIDEO_SOUND_LANG1) {
s1 |= TDA8425_S1_ML_SOUND_A;
s1 |= TDA8425_S1_STEREO_PSEUDO;
} else if (mode & VIDEO_SOUND_LANG2) {
s1 |= TDA8425_S1_ML_SOUND_B;
s1 |= TDA8425_S1_STEREO_PSEUDO;
-
+
} else {
s1 |= TDA8425_S1_ML_STEREO;
-
+
if (mode & VIDEO_SOUND_MONO)
s1 |= TDA8425_S1_STEREO_MONO;
if (mode & VIDEO_SOUND_STEREO)
static int ta8874z_getmode(struct CHIPSTATE *chip)
{
int val, mode;
-
+
val = chip_read(chip);
mode = VIDEO_SOUND_MONO;
if (val & TA8874Z_B1){
int tda9873 = 1;
int tda9874a = 1;
int tea6300 = 0; // address clash with msp34xx
+int tea6320 = 0; // address clash with msp34xx
int tea6420 = 1;
int pic16c54 = 1;
int ta8874z = 0; // address clash with tda9840
-MODULE_PARM(tda8425,"i");
-MODULE_PARM(tda9840,"i");
-MODULE_PARM(tda9850,"i");
-MODULE_PARM(tda9855,"i");
-MODULE_PARM(tda9873,"i");
-MODULE_PARM(tda9874a,"i");
-MODULE_PARM(tea6300,"i");
-MODULE_PARM(tea6420,"i");
-MODULE_PARM(pic16c54,"i");
-MODULE_PARM(ta8874z,"i");
+module_param(tda8425, int, 0444);
+module_param(tda9840, int, 0444);
+module_param(tda9850, int, 0444);
+module_param(tda9855, int, 0444);
+module_param(tda9873, int, 0444);
+module_param(tda9874a, int, 0444);
+module_param(tea6300, int, 0444);
+module_param(tea6320, int, 0444);
+module_param(tea6420, int, 0444);
+module_param(pic16c54, int, 0444);
+module_param(ta8874z, int, 0444);
static struct CHIPDESC chiplist[] = {
{
.inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE,
.inputmap = {0xa0, 0xa2, 0xa0, 0xa0, 0xc0},
.inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE,
-
+
},
{
.name = "tda9874h/a",
.inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
.inputmute = TEA6300_S_GMU,
},
+ {
+ .name = "tea6320",
+ .id = I2C_DRIVERID_TEA6300,
+ .initialize = tea6320_initialize,
+ .insmodopt = &tea6320,
+ .addr_lo = I2C_TEA6300 >> 1,
+ .addr_hi = I2C_TEA6300 >> 1,
+ .registers = 8,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TEA6320_V,
+ .rightreg = TEA6320_V,
+ .bassreg = TEA6320_BA,
+ .treblereg = TEA6320_TR,
+ .volfunc = tea6320_volume,
+ .bassfunc = tea6320_shift11,
+ .treblefunc = tea6320_shift11,
+
+ .inputreg = TEA6320_S,
+ .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD },
+ .inputmute = TEA6300_S_GMU,
+ },
{
.name = "tea6420",
.id = I2C_DRIVERID_TEA6420,
static int chip_probe(struct i2c_adapter *adap)
{
+ /* don't attach on saa7146 based cards,
+ because dedicated drivers are used */
+ if ((adap->id & I2C_ALGO_SAA7146))
+ return 0;
#ifdef I2C_CLASS_TV_ANALOG
if (adap->class & I2C_CLASS_TV_ANALOG)
return i2c_probe(adap, &addr_data, chip_attach);
wake_up_interruptible(&chip->wq);
wait_for_completion(&chip->texit);
}
-
+
i2c_detach_client(&chip->c);
kfree(chip);
return 0;
if (desc->flags & CHIP_HAS_VOLUME) {
va->flags |= VIDEO_AUDIO_VOLUME;
va->volume = max(chip->left,chip->right);
- va->balance = (32768*min(chip->left,chip->right))/
- (va->volume ? va->volume : 1);
+ if (va->volume)
+ va->balance = (32768*min(chip->left,chip->right))/
+ va->volume;
+ else
+ va->balance = 32768;
}
if (desc->flags & CHIP_HAS_BASSTREBLE) {
va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
case VIDIOCSAUDIO:
{
struct video_audio *va = arg;
-
+
if (desc->flags & CHIP_HAS_VOLUME) {
chip->left = (min(65536 - va->balance,32768) *
va->volume) / 32768;
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
-
+
dprintk(KERN_DEBUG "tvaudio: VIDIOCSCHAN\n");
chip->norm = vc->norm;
break;