diff options
author | Gerd Knorr <devnull@localhost> | 2004-09-30 13:47:25 +0000 |
---|---|---|
committer | Gerd Knorr <devnull@localhost> | 2004-09-30 13:47:25 +0000 |
commit | d726b96eaf915bc2d78d9bceb317ca5bf8669ddd (patch) | |
tree | 9acb09fb177b5c3b25d46e64fc56390697352201 /linux/drivers/media/video | |
parent | a550d1ffb13c412ef15534bf63949634708c0f01 (diff) | |
download | mediapointer-dvb-s2-d726b96eaf915bc2d78d9bceb317ca5bf8669ddd.tar.gz mediapointer-dvb-s2-d726b96eaf915bc2d78d9bceb317ca5bf8669ddd.tar.bz2 |
- tuner: fix entries.
- msp3400: merge with ivtv -- part one (thanks to David George).
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r-- | linux/drivers/media/video/msp3400-driver.c | 619 | ||||
-rw-r--r-- | linux/drivers/media/video/msp3400.c | 619 | ||||
-rw-r--r-- | linux/drivers/media/video/msp3400.h | 22 |
3 files changed, 1066 insertions, 194 deletions
diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 74e57e172..6f930254c 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -61,14 +61,23 @@ #include "msp3400.h" #include "compat.h" +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_SIMPLE 1 /* use short programming (>= msp3410 only) */ +#define OPMODE_SIMPLER 2 /* use shorter programming (>= msp34xxG) */ + /* insmod parameters */ +static int opmode = OPMODE_AUTO; static int debug = 0; /* debug output */ static int once = 0; /* no continous stereo monitoring */ static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), - the autoscan seems work well only with FM... */ -static int simple = -1; /* use short programming (>= msp3410 only) */ + the autoscan seems work well only with FM... */ +static int standard = 1; /* Override auto detect of audio standard, if needed. */ static int dolby = 0; +static int stereo_threshold = 0x190; /* a2 threshold for stereo/bilingual + (msp34xxg only) 0x00a0-0x03c0 */ + #define DFP_COUNT 0x41 static const int bl_dfp[] = { 0x00, 0x01, 0x02, 0x03, 0x06, 0x08, 0x09, 0x0a, @@ -78,7 +87,7 @@ static const int bl_dfp[] = { struct msp3400c { int rev1,rev2; - int simple; + int opmode; int mode; int norm; int stereo; @@ -86,6 +95,7 @@ struct msp3400c { int acb; int main, second; /* sound carrier */ int input; + int source; /* see msp34xxg_set_source */ int muted; int volume, balance; @@ -102,6 +112,9 @@ struct msp3400c { int active:1; int restart:1; int rmmod:1; +#if 0 + struct semaphore *notify; +#endif int watch_stereo; struct timer_list wake_stereo; @@ -109,6 +122,7 @@ struct msp3400c { #define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) #define HAVE_SIMPLE(msp) ((msp->rev1 & 0xff) >= 'D'-'@') +#define HAVE_SIMPLER(msp) ((msp->rev1 & 0xff) >= 'G'-'@') #define HAVE_RADIO(msp) ((msp->rev1 & 0xff) >= 'G'-'@') #define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ @@ -119,9 +133,15 @@ struct msp3400c { #define d2printk if (debug >= 2) printk MODULE_PARM(once,"i"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); MODULE_PARM(debug,"i"); -MODULE_PARM(simple,"i"); +MODULE_PARM_DESC(debug, "Enable debug messages"); +MODULE_PARM(opmode,"i"); +MODULE_PARM(stereo_threshold,"i"); +MODULE_PARM(standard,"i"); +MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); MODULE_PARM(amsound,"i"); +MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); MODULE_PARM(dolby,"i"); MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); @@ -205,7 +225,7 @@ msp3400c_read(struct i2c_client *client, int dev, int addr) msp3400c_reset(client); return -1; } - return read[0] << 8 | read[1]; + return read[0] << 8 | read[1]; } static int @@ -336,16 +356,6 @@ static struct CARRIER_DETECT carrier_detect_65[] = { /* ----------------------------------------------------------------------- */ -#define SCART_MASK 0 -#define SCART_IN1 1 -#define SCART_IN2 2 -#define SCART_IN1_DA 3 -#define SCART_IN2_DA 4 -#define SCART_IN3 5 -#define SCART_IN4 6 -#define SCART_MONO 7 -#define SCART_MUTE 8 - static int scarts[3][9] = { /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, @@ -399,8 +409,8 @@ static void msp3400c_setvolume(struct i2c_client *client, muted ? "on" : "off", volume, balance, val>>8, bal); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ - /* scart - on/off only */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, + muted ? 0x01 : (val | 0x01)); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, bal << 8); } @@ -477,6 +487,18 @@ static void msp3400c_setmode(struct i2c_client *client, int type) } } +// given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask +static int best_audio_mode(int mode) +{ + if (mode & VIDEO_SOUND_STEREO) + return VIDEO_SOUND_STEREO; + if (mode & VIDEO_SOUND_LANG1) + return VIDEO_SOUND_LANG1; + if (mode & VIDEO_SOUND_LANG2) + return VIDEO_SOUND_LANG2; + return VIDEO_SOUND_MONO; +} + /* turn on/off nicam + stereo */ static void msp3400c_setstereo(struct i2c_client *client, int mode) { @@ -493,6 +515,8 @@ static void msp3400c_setstereo(struct i2c_client *client, int mode) int nicam=0; /* channel source: FM/AM or nicam */ int src=0; + BUG_ON(msp->opmode == OPMODE_SIMPLER); + /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: @@ -628,6 +652,18 @@ msp3400c_restore_dfp(struct i2c_client *client) } } +/* if the dfp_regs is set, set what's in there. + * Otherwise, set the default value */ +static int msp3400c_write_dfp_with_default(struct i2c_client *client, int addr, + int default_value) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int value=default_value; + if ( addr< DFP_COUNT && -1 != msp->dfp_regs[addr] ) + value=msp->dfp_regs[addr]; + return msp3400c_write(client, I2C_MSP3400C_DFP, addr, value); +} + /* ----------------------------------------------------------------------- */ struct REGISTER_DUMP { @@ -767,16 +803,8 @@ static void watch_stereo(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - if (autodetect_stereo(client)) { - if (msp->stereo & VIDEO_SOUND_STEREO) - msp3400c_setstereo(client,VIDEO_SOUND_STEREO); - else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(client,VIDEO_SOUND_LANG1); - else if (msp->stereo & VIDEO_SOUND_LANG2) - msp3400c_setstereo(client,VIDEO_SOUND_LANG2); - else - msp3400c_setstereo(client,VIDEO_SOUND_MONO); - } + if (autodetect_stereo(client)) + msp3400c_setstereo(client,best_audio_mode(msp->stereo)); if (once) msp->watch_stereo = 0; if (msp->watch_stereo) @@ -867,10 +895,12 @@ static int msp3400c_thread(void *data) /* carrier detect pass #2 -- second (stereo) carrier */ switch (max1) { case 1: /* 5.5 */ - cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); + cd = carrier_detect_55; + count = CARRIER_COUNT(carrier_detect_55); break; case 3: /* 6.5 */ - cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); + cd = carrier_detect_65; + count = CARRIER_COUNT(carrier_detect_65); break; case 0: /* 4.5 */ case 2: /* 6.0 */ @@ -988,7 +1018,7 @@ static int msp3400c_thread(void *data) done: msp->active = 0; dprintk(KERN_DEBUG "msp3400: thread: exit\n"); - complete_and_exit(&msp->texit, 0); + complete_and_exit(&msp->texit, 0); } /* ----------------------------------------------------------------------- */ @@ -1021,6 +1051,49 @@ static struct MODES { { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, { -1, 0, 0, NULL }, /* EOF */ }; + +static inline const char *msp34xx_standard_mode_name(int mode) +{ + int i; + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == mode) + return modelist[i].name; + return "unknown"; +} + +static int msp34xx_modus(int norm) +{ + switch (norm) { + case VIDEO_MODE_PAL: + return 0x1003; + case VIDEO_MODE_NTSC: /* BTSC */ + return 0x2003; + case VIDEO_MODE_SECAM: + return 0x0003; + case VIDEO_MODE_RADIO: + return 0x0003; + case VIDEO_MODE_AUTO: + return 0x2003; + default: + return 0x0003; + } +} + +static int msp34xx_standard(int norm) +{ + switch (norm) { + case VIDEO_MODE_PAL: + return 1; + case VIDEO_MODE_NTSC: /* BTSC */ + return 0x0020; + case VIDEO_MODE_SECAM: + return 1; + case VIDEO_MODE_RADIO: + return 0x0040; + default: + return 1; + } +} static int msp3410d_thread(void *data) { @@ -1074,39 +1147,14 @@ static int msp3410d_thread(void *data) msp3400c_reset(client); /* start autodetect */ - switch (msp->norm) { - case VIDEO_MODE_PAL: - mode = 0x1003; - std = 1; - break; - case VIDEO_MODE_NTSC: /* BTSC */ - mode = 0x2003; - std = 0x0020; - break; - case VIDEO_MODE_SECAM: - mode = 0x0003; - std = 1; - break; - case VIDEO_MODE_RADIO: - mode = 0x0003; - std = 0x0040; - break; - default: - mode = 0x0003; - std = 1; - break; - } + mode = msp34xx_modus(msp->norm); + std = msp34xx_standard(msp->norm); msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); - if (debug) { - int i; - for (i = 0; modelist[i].name != NULL; i++) - if (modelist[i].retval == std) - break; + if (debug) printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", - modelist[i].name ? modelist[i].name : "unknown",std); - } + msp34xx_standard_mode_name(std) ,std); if (std != 1) { /* programmed some specific mode */ @@ -1211,12 +1259,14 @@ static int msp3410d_thread(void *data) msp->watch_stereo = 1; break; } - - /* unmute + restore dfp registers */ + + /* unmute, restore misc registers */ msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); + + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); msp3400c_restore_dfp(client); if (msp->watch_stereo) @@ -1233,6 +1283,296 @@ done: } /* ----------------------------------------------------------------------- */ +/* msp34xxG + (simpler no-thread) */ +/* this one uses both automatic standard detection and automatic sound */ +/* select which are available in the newer G versions */ +/* struct msp: only norm, acb and source are really used in this mode */ + +static void msp34xxg_set_source(struct i2c_client *client, int source); + +/* (re-)initialize the msp34xxg, according to the current norm in msp->norm + * return 0 if it worked, -1 if it failed + */ +static int msp34xxg_reset(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int i; + int modus; + int std; + + if (msp3400c_reset(client)) + return -1; + + /* make sure that input/output is muted (paranoid mode) */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + 0x0f20 /* mute DSP input, mute SCART 1 */)) + return -1; + + /* step-by-step initialisation, as described in the manual */ + modus = msp34xx_modus(msp->norm); + modus &= ~0x03; /* STATUS_CHANGE=0 */ + modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION=1 */ + if (msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x30/*MODUS*/, + modus)) + return -1; + + /* write the dfps that may have an influence on standard/audio autodetection right now */ + msp34xxg_set_source(client, msp->source); + + if (msp3400c_write_dfp_with_default(client, + 0x0e, /* AM/FM Prescale */ + 0x3000 /* default: [15:8] 75khz deviation */)) + return -1; + + if (msp3400c_write_dfp_with_default(client, + 0x10, /* NICAM Prescale */ + 0x5a00 /* default: 9db gain (as recommended) */)) + return -1; + + std = standard; + if (msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x20, /* STANDARD SELECT */ + std /* default: 0x01 for automatic standard select*/)) + return -1; + + + if (std == 0x01) { + dprintk("msp34xxg: triggered autodetect, waiting for result\n"); + + /* triggered autodetect */ + for (i = 0; i < 10; i++) { + int val; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + return -1; /* failed */ + + /* check results */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); + if (val < 0x07ff) { + std=val; + break; + } + dprintk("msp34xxg: detection still in progress\n"); + } + if (i == 10) { + dprintk("msp34xxg: detection still in progress after 10 tries. giving up.\n"); + return -1; + } + } + dprintk("msp34xxg: current mode: %s (0x%04x)\n", + msp34xx_standard_mode_name(std), std); + + /* unmute: dispatch sound to scart output, set scart volume */ + dprintk("msp34xxg: unmute\n"); + + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); + + /* restore other dfp's */ + msp3400c_restore_dfp(client); + + /* restore ACB */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + msp->acb)) + return -1; + + return 0; +} + +/* set the same 'source' for the loudspeaker, scart and quasi-peak detector + * the value for source is the same as bit 15:8 of DFP registers 0x08, + * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B + * + * this function replaces msp3400c_setstereo + */ +static void msp34xxg_set_source(struct i2c_client *client, int source) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + /* fix matrix mode to stereo and let the msp choose what + * to output according to 'source', as recommended + */ + int value = (source&0x07)<<8|(source==0 ? 0x00:0x20); + dprintk("msp34xxg: set source to %d (0x%x)\n", source, value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x08, /* Loudspeaker Output */ + value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x0a, /* SCART1 DA Output */ + value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x0c, /* Quasi-peak detector */ + value); + /* + * set identification threshold. Personally, I + * I set it to a higher value that the default + * of 0x190 to ignore noisy stereo signals. + * this needs tuning. (recommended range 0x00a0-0x03c0) + * 0x7f0 = forced mono mode + */ + msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x22, /* a2 threshold for stereo/bilingual */ + source==0 ? 0x7f0:stereo_threshold); + msp->source=source; +} + +/* get the current stereo mode and return it as a V4L1 stereo value (Use V4L2 calls whenever possible) */ +static int msp34xxg_get_v4l1_stereo(struct i2c_client *client) +{ + /* This is not really clear: the API says that the mode should + * be the current mode, but the old driver returned what the + * mode *could* be (like rxsubchans in v4l2). I'll just + * follow what the API says... + * + * The most important point is, I think, that if someone does a get + * and then a set with this value, nothing should have changed. It + * used not to be the case and that was extremely confusing: one would + * do a GET, change mute to 1, then a SET with the same pointer and the + * stereo mode would have changed, the watcher thread killed... (see + * handler for VIDIOCSAUDIO) + */ + struct msp3400c *msp = i2c_get_clientdata(client); + switch (msp->source) { + case 1: /* stereo or A|B */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2|VIDEO_SOUND_STEREO; + case 3: /* stereo or A */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_STEREO; + case 4: /* stereo or B */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_STEREO; + case 0: /* mono */ + return VIDEO_SOUND_MONO; + default: /* scart input */ + return VIDEO_SOUND_STEREO; + } +} + +/* set the current stereo mode given a v4l1 mode (Use V4L2 calls whenever possible) */ +static void msp34xxg_set_v4l1_stereo(struct i2c_client *client, int stereo) +{ + int source; + + if ((stereo & VIDEO_SOUND_LANG1) && (stereo & VIDEO_SOUND_LANG2)) + source = 1; /* stereo or A|B */ + else if (stereo & VIDEO_SOUND_LANG1) + source = 3; /* stereo or A */ + else if (stereo & VIDEO_SOUND_LANG2) + source = 4; /* stereo or B */ + else if (stereo & VIDEO_SOUND_STEREO) + source = 3; /* stereo or A */ + else + source = 0; /* mono only */ + + msp34xxg_set_source(client, source); +} + +#if 0 +static void msp34xxg_get_v4l2_stereo(struct i2c_client *client, int *rxsubchans, + int *audmode) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + if (rxsubchans) { + int status = msp3400c_read(client, + I2C_MSP3400C_DEM, + 0x0200 /* STATUS */); + int is_bilingual = status&0x100; + int is_stereo = status&0x40; + int val=0; + if (is_stereo) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + val |= V4L2_TUNER_SUB_LANG1|V4L2_TUNER_SUB_LANG2; + /* I'm supposed to check whether it's SAP or not + * and set only LANG2/SAP in this case. Yet, the MSP + * does a lot of work to hide this and handle everything + * the same way. I don't want to work around it so unless + * this is a problem, I'll handle SAP just like lang1/lang2. + */ + } + dprintk("msp34xxg: status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", + status, is_stereo, is_bilingual, val); + *rxsubchans=val; + } + if (audmode) { + int val; + switch (msp->source) { + case 0: /* mono only */ + val = V4L2_TUNER_MODE_MONO; + break; + + case 1: /* stereo or A|B */ + case 2: /* scart input (stereo) */ + /* I'm surprised, but according to v4l2 spec, that + * is what V4L2_TUNER_MODE_STEREO *may* mean: + * > When the tuner receives bilingual audio it may play + * > different languages on the left and right channel or + * > the primary language on both channels. + * I chose the 2nd interpretation since there's no + * BILINGUAL mode. + */ + val = V4L2_TUNER_MODE_STEREO; + break; + + default: + case 3: /* stereo or A */ + val = V4L2_TUNER_MODE_LANG1; + break; + case 4: /* stereo or B */ + val = V4L2_TUNER_MODE_LANG2; + break; + } + dprintk("msp34xxg: source=%d -> audmode=%d\n", + msp->source, val); + *audmode=val; + } +} + +static void msp34xxg_set_v4l2_stereo(struct i2c_client *client, int audmode) +{ + int source = 0; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + source=0; /* mono only */ + break; + + case V4L2_TUNER_MODE_STEREO: + source=1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ + /* problem: that could also mean 2 (scart input) */ + break; + + case V4L2_TUNER_MODE_LANG1: + source=3; /* stereo or A */ + break; + + case V4L2_TUNER_MODE_LANG2: + source=4; /* stereo or B */ + break; + + default: /* doing nothing: a safe, sane default */ + return; + } + + msp34xxg_set_source(client, source); +} +#endif + +/* ----------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind); @@ -1272,6 +1612,7 @@ static int msp_attach(struct i2c_adapter *adap, int addr, { struct msp3400c *msp; struct i2c_client *c; + int (*thread_func)(void *data) = NULL; int i; client_template.adapter = adap; @@ -1291,12 +1632,12 @@ static int msp_attach(struct i2c_adapter *adap, int addr, } memset(msp,0,sizeof(struct msp3400c)); - msp->volume = 65535; + msp->volume = 58880; /* 0db gain */ msp->balance = 32768; - msp->bass = 32768; - msp->treble = 32768; - msp->input = -1; - msp->muted = 1; + msp->bass = 32768; + msp->treble = 32768; + msp->input = -1; + msp->muted = 1; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; @@ -1330,12 +1671,14 @@ static int msp_attach(struct i2c_adapter *adap, int addr, (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@', ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); - if (simple == -1) { - /* default mode */ - msp->simple = HAVE_SIMPLE(msp); - } else { - /* use insmod option */ - msp->simple = simple; + msp->opmode = opmode; + if (OPMODE_AUTO == msp->opmode) { + if (HAVE_SIMPLER(msp)) + msp->opmode = OPMODE_SIMPLER; + else if (HAVE_SIMPLE(msp)) + msp->opmode = OPMODE_SIMPLE; + else + msp->opmode = OPMODE_MANUAL; } /* timer for stereo checking */ @@ -1349,22 +1692,40 @@ static int msp_attach(struct i2c_adapter *adap, int addr, printk(" +nicam"); if (HAVE_SIMPLE(msp)) printk(" +simple"); + if (HAVE_SIMPLER(msp)) + printk(" +simpler"); if (HAVE_RADIO(msp)) printk(" +radio"); printk("\n"); - /* startup control thread */ + /* version-specific initialization */ + switch (msp->opmode) { + case OPMODE_MANUAL: + thread_func = msp3400c_thread; + break; + case OPMODE_SIMPLE: + thread_func = msp3410d_thread; + break; + case OPMODE_SIMPLER: + msp34xxg_reset(c); + break; + } + + /* startup control thread if needed */ + if (thread_func) { + init_completion(&msp->texit); + msp->tpid = kernel_thread(thread_func, (void *)c, 0); + if (msp->tpid < 0) + printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); + wake_up_interruptible(&msp->wq); + } else { + msp->tpid = -1; + } + + /* done */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_INC_USE_COUNT; #endif - init_completion(&msp->texit); - msp->tpid = kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, - (void *)c, 0); - if (msp->tpid < 0) - printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); - wake_up_interruptible(&msp->wq); - - /* done */ i2c_attach_client(c); return 0; } @@ -1372,7 +1733,7 @@ static int msp_attach(struct i2c_adapter *adap, int addr, static int msp_detach(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - + /* shutdown control thread */ del_timer_sync(&msp->wake_stereo); if (msp->tpid >= 0) { @@ -1412,6 +1773,8 @@ static void msp_wake_thread(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); + if (-1 == msp->tpid) + return; msp3400c_setvolume(client,msp->muted,0,0); msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1462,7 +1825,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,scart,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - msp3400c_setstereo(client,msp->stereo); + if (msp->opmode != OPMODE_SIMPLER) + msp3400c_setstereo(client, msp->stereo); } if (msp->active) msp->restart = 1; @@ -1471,22 +1835,26 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case AUDC_SET_RADIO: dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; + dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); msp->watch_stereo=0; del_timer(&msp->wake_stereo); - dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); - if (msp->simple) { - /* the thread will do for us */ - msp_wake_thread(client); - } else { + switch (msp->opmode) { + case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ msp3400c_setmode(client,MSP_MODE_FM_RADIO); msp3400c_setcarrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); msp3400c_setvolume(client, msp->muted, - msp->volume, msp->balance); + msp->volume, msp->balance); + break; + case OPMODE_SIMPLE: + /* the thread will do for us */ + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; } - if (msp->active) - msp->restart = 1; break; #if 1 @@ -1536,7 +1904,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) va->bass = msp->bass; va->treble = msp->treble; - if (msp->norm != VIDEO_MODE_RADIO) { + if (msp->opmode == OPMODE_SIMPLER) { + va->mode = msp34xxg_get_v4l1_stereo(client); + } else if (msp->norm != VIDEO_MODE_RADIO) { autodetect_stereo(client); va->mode = msp->stereo; } @@ -1559,10 +1929,14 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp3400c_settreble(client,msp->treble); if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) { - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); - msp->stereo = va->mode & 0x0f; - msp3400c_setstereo(client,va->mode & 0x0f); + if (msp->opmode == OPMODE_SIMPLER) { + msp34xxg_set_v4l1_stereo(client, va->mode); + } else { + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + msp->stereo = va->mode & 0x0f; + msp3400c_setstereo(client,va->mode & 0x0f); + } } break; } @@ -1572,13 +1946,64 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); msp->norm = vc->norm; + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } break; } case VIDIOCSFREQ: { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); - msp_wake_thread(client); + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } + break; + } + +#if 0 + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + vt->capability|=V4L2_TUNER_CAP_STEREO|V4L2_TUNER_CAP_LANG1|V4L2_TUNER_CAP_LANG2; + + /* get rxsubchans and audmode */ + if (IS_MSP34XX_G(msp)) { + msp34xxg_get_v4l2_stereo(client, &vt->rxsubchans, &vt->audmode); + } + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + /* only set audmode */ + if (vt->audmode!=-1) { + if (IS_MSP34XX_G(msp)) { + msp34xxg_set_v4l2_stereo(client, vt->audmode); + } + } + break; + } +#endif + + case MSP_SET_MATRIX: + { + struct msp_matrix *mspm = arg; + + dprintk(KERN_DEBUG "msp34xx: MSP_SET_MATRIX\n"); + msp3400c_set_scart(client, mspm->input, mspm->output); break; } diff --git a/linux/drivers/media/video/msp3400.c b/linux/drivers/media/video/msp3400.c index 74e57e172..6f930254c 100644 --- a/linux/drivers/media/video/msp3400.c +++ b/linux/drivers/media/video/msp3400.c @@ -61,14 +61,23 @@ #include "msp3400.h" #include "compat.h" +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_SIMPLE 1 /* use short programming (>= msp3410 only) */ +#define OPMODE_SIMPLER 2 /* use shorter programming (>= msp34xxG) */ + /* insmod parameters */ +static int opmode = OPMODE_AUTO; static int debug = 0; /* debug output */ static int once = 0; /* no continous stereo monitoring */ static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), - the autoscan seems work well only with FM... */ -static int simple = -1; /* use short programming (>= msp3410 only) */ + the autoscan seems work well only with FM... */ +static int standard = 1; /* Override auto detect of audio standard, if needed. */ static int dolby = 0; +static int stereo_threshold = 0x190; /* a2 threshold for stereo/bilingual + (msp34xxg only) 0x00a0-0x03c0 */ + #define DFP_COUNT 0x41 static const int bl_dfp[] = { 0x00, 0x01, 0x02, 0x03, 0x06, 0x08, 0x09, 0x0a, @@ -78,7 +87,7 @@ static const int bl_dfp[] = { struct msp3400c { int rev1,rev2; - int simple; + int opmode; int mode; int norm; int stereo; @@ -86,6 +95,7 @@ struct msp3400c { int acb; int main, second; /* sound carrier */ int input; + int source; /* see msp34xxg_set_source */ int muted; int volume, balance; @@ -102,6 +112,9 @@ struct msp3400c { int active:1; int restart:1; int rmmod:1; +#if 0 + struct semaphore *notify; +#endif int watch_stereo; struct timer_list wake_stereo; @@ -109,6 +122,7 @@ struct msp3400c { #define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) #define HAVE_SIMPLE(msp) ((msp->rev1 & 0xff) >= 'D'-'@') +#define HAVE_SIMPLER(msp) ((msp->rev1 & 0xff) >= 'G'-'@') #define HAVE_RADIO(msp) ((msp->rev1 & 0xff) >= 'G'-'@') #define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ @@ -119,9 +133,15 @@ struct msp3400c { #define d2printk if (debug >= 2) printk MODULE_PARM(once,"i"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); MODULE_PARM(debug,"i"); -MODULE_PARM(simple,"i"); +MODULE_PARM_DESC(debug, "Enable debug messages"); +MODULE_PARM(opmode,"i"); +MODULE_PARM(stereo_threshold,"i"); +MODULE_PARM(standard,"i"); +MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); MODULE_PARM(amsound,"i"); +MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); MODULE_PARM(dolby,"i"); MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); @@ -205,7 +225,7 @@ msp3400c_read(struct i2c_client *client, int dev, int addr) msp3400c_reset(client); return -1; } - return read[0] << 8 | read[1]; + return read[0] << 8 | read[1]; } static int @@ -336,16 +356,6 @@ static struct CARRIER_DETECT carrier_detect_65[] = { /* ----------------------------------------------------------------------- */ -#define SCART_MASK 0 -#define SCART_IN1 1 -#define SCART_IN2 2 -#define SCART_IN1_DA 3 -#define SCART_IN2_DA 4 -#define SCART_IN3 5 -#define SCART_IN4 6 -#define SCART_MONO 7 -#define SCART_MUTE 8 - static int scarts[3][9] = { /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, @@ -399,8 +409,8 @@ static void msp3400c_setvolume(struct i2c_client *client, muted ? "on" : "off", volume, balance, val>>8, bal); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ - /* scart - on/off only */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, + muted ? 0x01 : (val | 0x01)); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, bal << 8); } @@ -477,6 +487,18 @@ static void msp3400c_setmode(struct i2c_client *client, int type) } } +// given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask +static int best_audio_mode(int mode) +{ + if (mode & VIDEO_SOUND_STEREO) + return VIDEO_SOUND_STEREO; + if (mode & VIDEO_SOUND_LANG1) + return VIDEO_SOUND_LANG1; + if (mode & VIDEO_SOUND_LANG2) + return VIDEO_SOUND_LANG2; + return VIDEO_SOUND_MONO; +} + /* turn on/off nicam + stereo */ static void msp3400c_setstereo(struct i2c_client *client, int mode) { @@ -493,6 +515,8 @@ static void msp3400c_setstereo(struct i2c_client *client, int mode) int nicam=0; /* channel source: FM/AM or nicam */ int src=0; + BUG_ON(msp->opmode == OPMODE_SIMPLER); + /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: @@ -628,6 +652,18 @@ msp3400c_restore_dfp(struct i2c_client *client) } } +/* if the dfp_regs is set, set what's in there. + * Otherwise, set the default value */ +static int msp3400c_write_dfp_with_default(struct i2c_client *client, int addr, + int default_value) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int value=default_value; + if ( addr< DFP_COUNT && -1 != msp->dfp_regs[addr] ) + value=msp->dfp_regs[addr]; + return msp3400c_write(client, I2C_MSP3400C_DFP, addr, value); +} + /* ----------------------------------------------------------------------- */ struct REGISTER_DUMP { @@ -767,16 +803,8 @@ static void watch_stereo(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - if (autodetect_stereo(client)) { - if (msp->stereo & VIDEO_SOUND_STEREO) - msp3400c_setstereo(client,VIDEO_SOUND_STEREO); - else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(client,VIDEO_SOUND_LANG1); - else if (msp->stereo & VIDEO_SOUND_LANG2) - msp3400c_setstereo(client,VIDEO_SOUND_LANG2); - else - msp3400c_setstereo(client,VIDEO_SOUND_MONO); - } + if (autodetect_stereo(client)) + msp3400c_setstereo(client,best_audio_mode(msp->stereo)); if (once) msp->watch_stereo = 0; if (msp->watch_stereo) @@ -867,10 +895,12 @@ static int msp3400c_thread(void *data) /* carrier detect pass #2 -- second (stereo) carrier */ switch (max1) { case 1: /* 5.5 */ - cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); + cd = carrier_detect_55; + count = CARRIER_COUNT(carrier_detect_55); break; case 3: /* 6.5 */ - cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); + cd = carrier_detect_65; + count = CARRIER_COUNT(carrier_detect_65); break; case 0: /* 4.5 */ case 2: /* 6.0 */ @@ -988,7 +1018,7 @@ static int msp3400c_thread(void *data) done: msp->active = 0; dprintk(KERN_DEBUG "msp3400: thread: exit\n"); - complete_and_exit(&msp->texit, 0); + complete_and_exit(&msp->texit, 0); } /* ----------------------------------------------------------------------- */ @@ -1021,6 +1051,49 @@ static struct MODES { { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, { -1, 0, 0, NULL }, /* EOF */ }; + +static inline const char *msp34xx_standard_mode_name(int mode) +{ + int i; + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == mode) + return modelist[i].name; + return "unknown"; +} + +static int msp34xx_modus(int norm) +{ + switch (norm) { + case VIDEO_MODE_PAL: + return 0x1003; + case VIDEO_MODE_NTSC: /* BTSC */ + return 0x2003; + case VIDEO_MODE_SECAM: + return 0x0003; + case VIDEO_MODE_RADIO: + return 0x0003; + case VIDEO_MODE_AUTO: + return 0x2003; + default: + return 0x0003; + } +} + +static int msp34xx_standard(int norm) +{ + switch (norm) { + case VIDEO_MODE_PAL: + return 1; + case VIDEO_MODE_NTSC: /* BTSC */ + return 0x0020; + case VIDEO_MODE_SECAM: + return 1; + case VIDEO_MODE_RADIO: + return 0x0040; + default: + return 1; + } +} static int msp3410d_thread(void *data) { @@ -1074,39 +1147,14 @@ static int msp3410d_thread(void *data) msp3400c_reset(client); /* start autodetect */ - switch (msp->norm) { - case VIDEO_MODE_PAL: - mode = 0x1003; - std = 1; - break; - case VIDEO_MODE_NTSC: /* BTSC */ - mode = 0x2003; - std = 0x0020; - break; - case VIDEO_MODE_SECAM: - mode = 0x0003; - std = 1; - break; - case VIDEO_MODE_RADIO: - mode = 0x0003; - std = 0x0040; - break; - default: - mode = 0x0003; - std = 1; - break; - } + mode = msp34xx_modus(msp->norm); + std = msp34xx_standard(msp->norm); msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); - if (debug) { - int i; - for (i = 0; modelist[i].name != NULL; i++) - if (modelist[i].retval == std) - break; + if (debug) printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", - modelist[i].name ? modelist[i].name : "unknown",std); - } + msp34xx_standard_mode_name(std) ,std); if (std != 1) { /* programmed some specific mode */ @@ -1211,12 +1259,14 @@ static int msp3410d_thread(void *data) msp->watch_stereo = 1; break; } - - /* unmute + restore dfp registers */ + + /* unmute, restore misc registers */ msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); + + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); msp3400c_restore_dfp(client); if (msp->watch_stereo) @@ -1233,6 +1283,296 @@ done: } /* ----------------------------------------------------------------------- */ +/* msp34xxG + (simpler no-thread) */ +/* this one uses both automatic standard detection and automatic sound */ +/* select which are available in the newer G versions */ +/* struct msp: only norm, acb and source are really used in this mode */ + +static void msp34xxg_set_source(struct i2c_client *client, int source); + +/* (re-)initialize the msp34xxg, according to the current norm in msp->norm + * return 0 if it worked, -1 if it failed + */ +static int msp34xxg_reset(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int i; + int modus; + int std; + + if (msp3400c_reset(client)) + return -1; + + /* make sure that input/output is muted (paranoid mode) */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + 0x0f20 /* mute DSP input, mute SCART 1 */)) + return -1; + + /* step-by-step initialisation, as described in the manual */ + modus = msp34xx_modus(msp->norm); + modus &= ~0x03; /* STATUS_CHANGE=0 */ + modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION=1 */ + if (msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x30/*MODUS*/, + modus)) + return -1; + + /* write the dfps that may have an influence on standard/audio autodetection right now */ + msp34xxg_set_source(client, msp->source); + + if (msp3400c_write_dfp_with_default(client, + 0x0e, /* AM/FM Prescale */ + 0x3000 /* default: [15:8] 75khz deviation */)) + return -1; + + if (msp3400c_write_dfp_with_default(client, + 0x10, /* NICAM Prescale */ + 0x5a00 /* default: 9db gain (as recommended) */)) + return -1; + + std = standard; + if (msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x20, /* STANDARD SELECT */ + std /* default: 0x01 for automatic standard select*/)) + return -1; + + + if (std == 0x01) { + dprintk("msp34xxg: triggered autodetect, waiting for result\n"); + + /* triggered autodetect */ + for (i = 0; i < 10; i++) { + int val; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + return -1; /* failed */ + + /* check results */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); + if (val < 0x07ff) { + std=val; + break; + } + dprintk("msp34xxg: detection still in progress\n"); + } + if (i == 10) { + dprintk("msp34xxg: detection still in progress after 10 tries. giving up.\n"); + return -1; + } + } + dprintk("msp34xxg: current mode: %s (0x%04x)\n", + msp34xx_standard_mode_name(std), std); + + /* unmute: dispatch sound to scart output, set scart volume */ + dprintk("msp34xxg: unmute\n"); + + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); + + /* restore other dfp's */ + msp3400c_restore_dfp(client); + + /* restore ACB */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + msp->acb)) + return -1; + + return 0; +} + +/* set the same 'source' for the loudspeaker, scart and quasi-peak detector + * the value for source is the same as bit 15:8 of DFP registers 0x08, + * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B + * + * this function replaces msp3400c_setstereo + */ +static void msp34xxg_set_source(struct i2c_client *client, int source) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + /* fix matrix mode to stereo and let the msp choose what + * to output according to 'source', as recommended + */ + int value = (source&0x07)<<8|(source==0 ? 0x00:0x20); + dprintk("msp34xxg: set source to %d (0x%x)\n", source, value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x08, /* Loudspeaker Output */ + value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x0a, /* SCART1 DA Output */ + value); + msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x0c, /* Quasi-peak detector */ + value); + /* + * set identification threshold. Personally, I + * I set it to a higher value that the default + * of 0x190 to ignore noisy stereo signals. + * this needs tuning. (recommended range 0x00a0-0x03c0) + * 0x7f0 = forced mono mode + */ + msp3400c_write(client, + I2C_MSP3400C_DEM, + 0x22, /* a2 threshold for stereo/bilingual */ + source==0 ? 0x7f0:stereo_threshold); + msp->source=source; +} + +/* get the current stereo mode and return it as a V4L1 stereo value (Use V4L2 calls whenever possible) */ +static int msp34xxg_get_v4l1_stereo(struct i2c_client *client) +{ + /* This is not really clear: the API says that the mode should + * be the current mode, but the old driver returned what the + * mode *could* be (like rxsubchans in v4l2). I'll just + * follow what the API says... + * + * The most important point is, I think, that if someone does a get + * and then a set with this value, nothing should have changed. It + * used not to be the case and that was extremely confusing: one would + * do a GET, change mute to 1, then a SET with the same pointer and the + * stereo mode would have changed, the watcher thread killed... (see + * handler for VIDIOCSAUDIO) + */ + struct msp3400c *msp = i2c_get_clientdata(client); + switch (msp->source) { + case 1: /* stereo or A|B */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2|VIDEO_SOUND_STEREO; + case 3: /* stereo or A */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_STEREO; + case 4: /* stereo or B */ + return VIDEO_SOUND_LANG1|VIDEO_SOUND_STEREO; + case 0: /* mono */ + return VIDEO_SOUND_MONO; + default: /* scart input */ + return VIDEO_SOUND_STEREO; + } +} + +/* set the current stereo mode given a v4l1 mode (Use V4L2 calls whenever possible) */ +static void msp34xxg_set_v4l1_stereo(struct i2c_client *client, int stereo) +{ + int source; + + if ((stereo & VIDEO_SOUND_LANG1) && (stereo & VIDEO_SOUND_LANG2)) + source = 1; /* stereo or A|B */ + else if (stereo & VIDEO_SOUND_LANG1) + source = 3; /* stereo or A */ + else if (stereo & VIDEO_SOUND_LANG2) + source = 4; /* stereo or B */ + else if (stereo & VIDEO_SOUND_STEREO) + source = 3; /* stereo or A */ + else + source = 0; /* mono only */ + + msp34xxg_set_source(client, source); +} + +#if 0 +static void msp34xxg_get_v4l2_stereo(struct i2c_client *client, int *rxsubchans, + int *audmode) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + if (rxsubchans) { + int status = msp3400c_read(client, + I2C_MSP3400C_DEM, + 0x0200 /* STATUS */); + int is_bilingual = status&0x100; + int is_stereo = status&0x40; + int val=0; + if (is_stereo) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + val |= V4L2_TUNER_SUB_LANG1|V4L2_TUNER_SUB_LANG2; + /* I'm supposed to check whether it's SAP or not + * and set only LANG2/SAP in this case. Yet, the MSP + * does a lot of work to hide this and handle everything + * the same way. I don't want to work around it so unless + * this is a problem, I'll handle SAP just like lang1/lang2. + */ + } + dprintk("msp34xxg: status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", + status, is_stereo, is_bilingual, val); + *rxsubchans=val; + } + if (audmode) { + int val; + switch (msp->source) { + case 0: /* mono only */ + val = V4L2_TUNER_MODE_MONO; + break; + + case 1: /* stereo or A|B */ + case 2: /* scart input (stereo) */ + /* I'm surprised, but according to v4l2 spec, that + * is what V4L2_TUNER_MODE_STEREO *may* mean: + * > When the tuner receives bilingual audio it may play + * > different languages on the left and right channel or + * > the primary language on both channels. + * I chose the 2nd interpretation since there's no + * BILINGUAL mode. + */ + val = V4L2_TUNER_MODE_STEREO; + break; + + default: + case 3: /* stereo or A */ + val = V4L2_TUNER_MODE_LANG1; + break; + case 4: /* stereo or B */ + val = V4L2_TUNER_MODE_LANG2; + break; + } + dprintk("msp34xxg: source=%d -> audmode=%d\n", + msp->source, val); + *audmode=val; + } +} + +static void msp34xxg_set_v4l2_stereo(struct i2c_client *client, int audmode) +{ + int source = 0; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + source=0; /* mono only */ + break; + + case V4L2_TUNER_MODE_STEREO: + source=1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ + /* problem: that could also mean 2 (scart input) */ + break; + + case V4L2_TUNER_MODE_LANG1: + source=3; /* stereo or A */ + break; + + case V4L2_TUNER_MODE_LANG2: + source=4; /* stereo or B */ + break; + + default: /* doing nothing: a safe, sane default */ + return; + } + + msp34xxg_set_source(client, source); +} +#endif + +/* ----------------------------------------------------------------------- */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind); @@ -1272,6 +1612,7 @@ static int msp_attach(struct i2c_adapter *adap, int addr, { struct msp3400c *msp; struct i2c_client *c; + int (*thread_func)(void *data) = NULL; int i; client_template.adapter = adap; @@ -1291,12 +1632,12 @@ static int msp_attach(struct i2c_adapter *adap, int addr, } memset(msp,0,sizeof(struct msp3400c)); - msp->volume = 65535; + msp->volume = 58880; /* 0db gain */ msp->balance = 32768; - msp->bass = 32768; - msp->treble = 32768; - msp->input = -1; - msp->muted = 1; + msp->bass = 32768; + msp->treble = 32768; + msp->input = -1; + msp->muted = 1; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; @@ -1330,12 +1671,14 @@ static int msp_attach(struct i2c_adapter *adap, int addr, (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@', ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); - if (simple == -1) { - /* default mode */ - msp->simple = HAVE_SIMPLE(msp); - } else { - /* use insmod option */ - msp->simple = simple; + msp->opmode = opmode; + if (OPMODE_AUTO == msp->opmode) { + if (HAVE_SIMPLER(msp)) + msp->opmode = OPMODE_SIMPLER; + else if (HAVE_SIMPLE(msp)) + msp->opmode = OPMODE_SIMPLE; + else + msp->opmode = OPMODE_MANUAL; } /* timer for stereo checking */ @@ -1349,22 +1692,40 @@ static int msp_attach(struct i2c_adapter *adap, int addr, printk(" +nicam"); if (HAVE_SIMPLE(msp)) printk(" +simple"); + if (HAVE_SIMPLER(msp)) + printk(" +simpler"); if (HAVE_RADIO(msp)) printk(" +radio"); printk("\n"); - /* startup control thread */ + /* version-specific initialization */ + switch (msp->opmode) { + case OPMODE_MANUAL: + thread_func = msp3400c_thread; + break; + case OPMODE_SIMPLE: + thread_func = msp3410d_thread; + break; + case OPMODE_SIMPLER: + msp34xxg_reset(c); + break; + } + + /* startup control thread if needed */ + if (thread_func) { + init_completion(&msp->texit); + msp->tpid = kernel_thread(thread_func, (void *)c, 0); + if (msp->tpid < 0) + printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); + wake_up_interruptible(&msp->wq); + } else { + msp->tpid = -1; + } + + /* done */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_INC_USE_COUNT; #endif - init_completion(&msp->texit); - msp->tpid = kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, - (void *)c, 0); - if (msp->tpid < 0) - printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); - wake_up_interruptible(&msp->wq); - - /* done */ i2c_attach_client(c); return 0; } @@ -1372,7 +1733,7 @@ static int msp_attach(struct i2c_adapter *adap, int addr, static int msp_detach(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - + /* shutdown control thread */ del_timer_sync(&msp->wake_stereo); if (msp->tpid >= 0) { @@ -1412,6 +1773,8 @@ static void msp_wake_thread(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); + if (-1 == msp->tpid) + return; msp3400c_setvolume(client,msp->muted,0,0); msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1462,7 +1825,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,scart,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - msp3400c_setstereo(client,msp->stereo); + if (msp->opmode != OPMODE_SIMPLER) + msp3400c_setstereo(client, msp->stereo); } if (msp->active) msp->restart = 1; @@ -1471,22 +1835,26 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case AUDC_SET_RADIO: dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; + dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); msp->watch_stereo=0; del_timer(&msp->wake_stereo); - dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); - if (msp->simple) { - /* the thread will do for us */ - msp_wake_thread(client); - } else { + switch (msp->opmode) { + case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ msp3400c_setmode(client,MSP_MODE_FM_RADIO); msp3400c_setcarrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); msp3400c_setvolume(client, msp->muted, - msp->volume, msp->balance); + msp->volume, msp->balance); + break; + case OPMODE_SIMPLE: + /* the thread will do for us */ + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; } - if (msp->active) - msp->restart = 1; break; #if 1 @@ -1536,7 +1904,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) va->bass = msp->bass; va->treble = msp->treble; - if (msp->norm != VIDEO_MODE_RADIO) { + if (msp->opmode == OPMODE_SIMPLER) { + va->mode = msp34xxg_get_v4l1_stereo(client); + } else if (msp->norm != VIDEO_MODE_RADIO) { autodetect_stereo(client); va->mode = msp->stereo; } @@ -1559,10 +1929,14 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp3400c_settreble(client,msp->treble); if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) { - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); - msp->stereo = va->mode & 0x0f; - msp3400c_setstereo(client,va->mode & 0x0f); + if (msp->opmode == OPMODE_SIMPLER) { + msp34xxg_set_v4l1_stereo(client, va->mode); + } else { + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + msp->stereo = va->mode & 0x0f; + msp3400c_setstereo(client,va->mode & 0x0f); + } } break; } @@ -1572,13 +1946,64 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); msp->norm = vc->norm; + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } break; } case VIDIOCSFREQ: { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); - msp_wake_thread(client); + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } + break; + } + +#if 0 + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + vt->capability|=V4L2_TUNER_CAP_STEREO|V4L2_TUNER_CAP_LANG1|V4L2_TUNER_CAP_LANG2; + + /* get rxsubchans and audmode */ + if (IS_MSP34XX_G(msp)) { + msp34xxg_get_v4l2_stereo(client, &vt->rxsubchans, &vt->audmode); + } + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + /* only set audmode */ + if (vt->audmode!=-1) { + if (IS_MSP34XX_G(msp)) { + msp34xxg_set_v4l2_stereo(client, vt->audmode); + } + } + break; + } +#endif + + case MSP_SET_MATRIX: + { + struct msp_matrix *mspm = arg; + + dprintk(KERN_DEBUG "msp34xx: MSP_SET_MATRIX\n"); + msp3400c_set_scart(client, mspm->input, mspm->output); break; } diff --git a/linux/drivers/media/video/msp3400.h b/linux/drivers/media/video/msp3400.h index 9673133f2..d70a954e1 100644 --- a/linux/drivers/media/video/msp3400.h +++ b/linux/drivers/media/video/msp3400.h @@ -8,7 +8,29 @@ struct msp_dfpreg { int value; }; +struct msp_matrix { + int input; + int output; +}; + #define MSP_SET_DFPREG _IOW('m',15,struct msp_dfpreg) #define MSP_GET_DFPREG _IOW('m',16,struct msp_dfpreg) +/* ioctl for MSP_SET_MATRIX will have to be registered */ +#define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix) + +#define SCART_MASK 0 +#define SCART_IN1 1 +#define SCART_IN2 2 +#define SCART_IN1_DA 3 +#define SCART_IN2_DA 4 +#define SCART_IN3 5 +#define SCART_IN4 6 +#define SCART_MONO 7 +#define SCART_MUTE 8 + +#define SCART_DSP_IN 0 +#define SCART1_OUT 1 +#define SCART2_OUT 2 + #endif /* MSP3400_H */ |