diff options
author | Gerd Knorr <devnull@localhost> | 2004-10-07 11:29:25 +0000 |
---|---|---|
committer | Gerd Knorr <devnull@localhost> | 2004-10-07 11:29:25 +0000 |
commit | f28d706bf875e195561dfbcf6d4e50947a0033c2 (patch) | |
tree | 32e4ada5eb7dca6aba06ab043fb117fea605d323 | |
parent | 5ed9c18ec57458d731d60be5304ccd25d6264da9 (diff) | |
download | mediapointer-dvb-s2-f28d706bf875e195561dfbcf6d4e50947a0033c2.tar.gz mediapointer-dvb-s2-f28d706bf875e195561dfbcf6d4e50947a0033c2.tar.bz2 |
- created a copy of the current msp3400 module (with 2.4 support).
- msp3400: 2.6-ified it (use kthread, throw away several ifdefs).
- msp3400: use a kernel thread for msp34xxg as well, so the std
detection runs asyncronly.
-rw-r--r-- | linux/drivers/media/video/msp3400-driver.c | 348 | ||||
-rw-r--r-- | linux/drivers/media/video/msp3400-old.c | 1945 | ||||
-rw-r--r-- | linux/drivers/media/video/msp3400.c | 348 | ||||
-rw-r--r-- | v4l/scripts/update | 4 |
4 files changed, 2187 insertions, 458 deletions
diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index c0f090a91..61b6fdcae 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -47,17 +47,12 @@ #include <linux/videodev.h> #include <linux/init.h> #include <linux/smp_lock.h> +#include <linux/kthread.h> #include <asm/semaphore.h> #include <asm/pgtable.h> -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -#include "audiochip.h" -#include "i2c-compat.h" -#include "id.h" -#else #include <media/audiochip.h> #include <media/id.h> -#endif #include "msp3400.h" #include "compat.h" @@ -99,16 +94,10 @@ struct msp3400c { int bass, treble; /* thread */ - pid_t tpid; - struct completion texit; + struct task_struct *kthread; wait_queue_head_t wq; - - int active:1; int restart:1; - int rmmod:1; - - int watch_stereo; - struct timer_list wake_stereo; + int watch_stereo:1; }; #define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) @@ -159,10 +148,6 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ -#ifndef I2C_M_IGNORE_NAK -# define I2C_M_IGNORE_NAK 0x1000 -#endif - static int msp3400c_reset(struct i2c_client *client) { /* reset and read revision code */ @@ -753,22 +738,22 @@ static int msp34xx_sleep(struct msp3400c *msp, int timeout) DECLARE_WAITQUEUE(wait, current); add_wait_queue(&msp->wq, &wait); - if (!msp->rmmod) { + if (!kthread_should_stop()) { if (timeout < 0) { set_current_state(TASK_INTERRUPTIBLE); schedule(); - } else + } else { +#if 0 + /* hmm, that one doesn't return after waking up msp->wq */ msleep_interruptible(timeout); +#else + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(timeout)); +#endif + } } remove_wait_queue(&msp->wq, &wait); - return msp->rmmod || signal_pending(current); -} - -static void msp3400c_stereo_wake(unsigned long data) -{ - struct msp3400c *msp = (struct msp3400c*)data; /* XXX alpha ??? */ - - wake_up_interruptible(&msp->wq); + return msp->restart; } /* stereo/multilang monitoring */ @@ -780,49 +765,27 @@ static void watch_stereo(struct i2c_client *client) msp3400c_set_audmode(client,best_audio_mode(msp->rxsubchans)); if (once) msp->watch_stereo = 0; - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+5*HZ); } static int msp3400c_thread(void *data) { struct i2c_client *client = data; struct msp3400c *msp = i2c_get_clientdata(client); - struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) - lock_kernel(); - daemonize(); - sigfillset(¤t->blocked); - strcpy(current->comm,"msp3400"); - unlock_kernel(); -#else - daemonize("msp3400"); - allow_signal(SIGTERM); -#endif - printk("msp3400: daemon started\n"); - + printk("msp3400: kthread started\n"); for (;;) { d2printk("msp3400: thread: sleep\n"); - if (msp34xx_sleep(msp,-1)) - goto done; - + msp34xx_sleep(msp,-1); d2printk("msp3400: thread: wakeup\n"); - msp->active = 1; - if (msp->watch_stereo) { - watch_stereo(client); - msp->active = 0; - continue; - } - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto done; - restart: + dprintk("msp3410: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; + if (VIDEO_MODE_RADIO == msp->norm || MSP_MODE_EXTERN == msp->mode) { /* no carrier scan, just unmute */ @@ -831,14 +794,18 @@ static int msp3400c_thread(void *data) msp->volume, msp->balance); continue; } - msp->restart = 0; + + /* mute */ msp3400c_setvolume(client, msp->muted, 0, 0); msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); val1 = val2 = 0; max1 = max2 = -1; - del_timer(&msp->wake_stereo); msp->watch_stereo = 0; + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto restart; + /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); @@ -851,12 +818,8 @@ static int msp3400c_thread(void *data) for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) - msp->restart = 0; - + goto restart; val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val > 32767) val -= 65536; @@ -888,12 +851,8 @@ static int msp3400c_thread(void *data) } for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) goto restart; - val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val > 32767) val -= 65536; @@ -974,23 +933,21 @@ static int msp3400c_thread(void *data) break; } - /* unmute + restore dfp registers */ + /* unmute */ msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); - - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+5*HZ); - if (debug) msp3400c_print_mode(msp); - - msp->active = 0; - } -done: - msp->active = 0; + /* monitor tv audio mode */ + while (msp->watch_stereo) { + if (msp34xx_sleep(msp,5000)) + goto restart; + watch_stereo(client); + } + } dprintk(KERN_DEBUG "msp3400: thread: exit\n"); - complete_and_exit(&msp->texit, 0); + return 0; } /* ----------------------------------------------------------------------- */ @@ -1073,37 +1030,18 @@ static int msp3410d_thread(void *data) struct msp3400c *msp = i2c_get_clientdata(client); int mode,val,i,std; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) - lock_kernel(); - daemonize(); - sigfillset(¤t->blocked); - strcpy(current->comm,"msp3410 [auto]"); - unlock_kernel(); -#else - daemonize("msp3410 [auto]"); - allow_signal(SIGTERM); -#endif printk("msp3410: daemon started\n"); - for (;;) { d2printk(KERN_DEBUG "msp3410: thread: sleep\n"); - if (msp34xx_sleep(msp,-1)) - goto done; - + msp34xx_sleep(msp,-1); d2printk(KERN_DEBUG "msp3410: thread: wakeup\n"); - msp->active = 1; - - if (msp->watch_stereo) { - watch_stereo(client); - msp->active = 0; - continue; - } - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto done; restart: + dprintk("msp3410: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; + if (msp->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ dprintk(KERN_DEBUG "msp3410: thread: no carrier scan\n"); @@ -1111,18 +1049,20 @@ static int msp3410d_thread(void *data) msp->volume, msp->balance); continue; } - msp->restart = 0; - del_timer(&msp->wake_stereo); - msp->watch_stereo = 0; /* put into sane state (and mute) */ msp3400c_reset(client); + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto restart; + /* start autodetect */ 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); + msp->watch_stereo = 0; if (debug) printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", @@ -1135,8 +1075,6 @@ static int msp3410d_thread(void *data) /* triggered autodetect */ for (;;) { if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) goto restart; /* check results */ @@ -1241,16 +1179,14 @@ static int msp3410d_thread(void *data) msp->volume, msp->balance); msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+HZ); - - msp->active = 0; + /* monitor tv audio mode */ + while (msp->watch_stereo) { + if (msp34xx_sleep(msp,5000)) + goto restart; + watch_stereo(client); + } } - -done: - msp->active = 0; dprintk(KERN_DEBUG "msp3410: thread: exit\n"); - complete_and_exit(&msp->texit, 0); return 0; } @@ -1265,12 +1201,10 @@ 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) +static int msp34xxg_init(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - int i; int modus; - int std; if (msp3400c_reset(client)) return -1; @@ -1306,55 +1240,76 @@ static int msp34xxg_reset(struct i2c_client *client) 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*/)) + standard /* default: 0x01 for automatic standard select*/)) return -1; + return 0; +} + +static int msp34xxg_thread(void *data) +{ + struct i2c_client *client = data; + struct msp3400c *msp = i2c_get_clientdata(client); + int val, std, i; + + printk("msp34xxg: daemon started\n"); + for (;;) { + d2printk(KERN_DEBUG "msp34xxg: thread: sleep\n"); + msp34xx_sleep(msp,-1); + d2printk(KERN_DEBUG "msp34xxg: thread: wakeup\n"); + restart: + dprintk("msp34xxg: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; - if (std == 0x01) { - dprintk("msp34xxg: triggered autodetect, waiting for result\n"); + /* setup the chip*/ + msp34xxg_init(client); + std = standard; + if (std != 0x01) + goto unmute; - /* triggered autodetect */ + /* watch autodetect */ + dprintk("msp34xxg: triggered autodetect, waiting for result\n"); for (i = 0; i < 10; i++) { - int val; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); - if (signal_pending(current)) - return -1; /* failed */ + if (msp34xx_sleep(msp,100)) + goto restart; /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) { - std=val; + std = val; break; } dprintk("msp34xxg: detection still in progress\n"); } - if (i == 10) { + if (0x01 == std) { dprintk("msp34xxg: detection still in progress after 10 tries. giving up.\n"); - return -1; + continue; } - } - 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 ACB */ - if (msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x13, /* ACB */ - msp->acb)) - return -1; + unmute: + 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 ACB */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + msp->acb)) + return -1; + } + dprintk(KERN_DEBUG "msp34xxg: thread: exit\n"); return 0; } @@ -1457,21 +1412,14 @@ static void msp34xxg_set_audmode(struct i2c_client *client, int audmode) /* ----------------------------------------------------------------------- */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind); -#else -static int msp_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind); -#endif static int msp_detach(struct i2c_client *client); static int msp_probe(struct i2c_adapter *adap); static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); static struct i2c_driver driver = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) .owner = THIS_MODULE, -#endif - .name = "i2c msp3400 driver", + .name = "i2c msp3400 driver", .id = I2C_DRIVERID_MSP3400, .flags = I2C_DF_NOTIFY, .attach_adapter = msp_probe, @@ -1486,12 +1434,7 @@ static struct i2c_client client_template = .driver = &driver, }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind) -#else -static int msp_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) -#endif { struct msp3400c *msp; struct i2c_client *c; @@ -1561,11 +1504,6 @@ static int msp_attach(struct i2c_adapter *adap, int addr, msp->opmode = OPMODE_MANUAL; } - /* timer for stereo checking */ - init_timer(&msp->wake_stereo); - msp->wake_stereo.function = msp3400c_stereo_wake; - msp->wake_stereo.data = (unsigned long)msp; - /* hello world :-) */ printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c)); if (HAVE_NICAM(msp)) @@ -1589,26 +1527,20 @@ static int msp_attach(struct i2c_adapter *adap, int addr, break; case OPMODE_SIMPLER: printk(" mode=simpler"); - msp34xxg_reset(c); + thread_func = msp34xxg_thread; break; } printk("\n"); /* 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) + msp->kthread = kthread_run(thread_func, c, "msp34xx"); + if (NULL == msp->kthread) 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 i2c_attach_client(c); return 0; } @@ -1618,37 +1550,22 @@ 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) { - msp->rmmod = 1; - wake_up_interruptible(&msp->wq); - wait_for_completion(&msp->texit); + if (msp->kthread >= 0) { + msp->restart = 1; + kthread_stop(msp->kthread); } msp3400c_reset(client); i2c_detach_client(client); kfree(msp); kfree(client); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#endif return 0; } static int msp_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, msp_attach); -#else - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3: - case I2C_ALGO_BIT | I2C_HW_B_BT848: - //case I2C_ALGO_SAA7134: - return i2c_probe(adap, &addr_data, msp_attach); - break; - } -#endif return 0; } @@ -1656,13 +1573,11 @@ static void msp_wake_thread(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - if (-1 == msp->tpid) + if (NULL == msp->kthread) return; msp3400c_setvolume(client,msp->muted,0,0); - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); - if (msp->active) - msp->restart = 1; + msp->watch_stereo = 0; + msp->restart = 1; wake_up_interruptible(&msp->wq); } @@ -1694,21 +1609,6 @@ static int mode_v4l1_to_v4l2(int mode) return V4L2_TUNER_MODE_MONO; } -static void msp_any_rescan(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - switch (msp->opmode) { - case OPMODE_MANUAL: - case OPMODE_SIMPLE: - msp_wake_thread(client); - break; - case OPMODE_SIMPLER: - msp34xxg_reset(client); - break; - } -} - static void msp_any_detect_stereo(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); @@ -1731,8 +1631,7 @@ static void msp_any_set_audmode(struct i2c_client *client, int audmode) switch (msp->opmode) { case OPMODE_MANUAL: case OPMODE_SIMPLE: - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; msp3400c_set_audmode(client, audmode); break; case OPMODE_SIMPLER: @@ -1772,7 +1671,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case AUDIO_TUNER: msp->mode = -1; - msp_wake_thread(client); break; default: if (*sarg & AUDIO_MUTE) @@ -1787,8 +1685,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (msp->opmode != OPMODE_SIMPLER) msp3400c_set_audmode(client, msp->audmode); } - if (msp->active) - msp->restart = 1; + msp_wake_thread(client); break; case AUDC_SET_RADIO: @@ -1796,7 +1693,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->norm = VIDEO_MODE_RADIO; dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); msp->watch_stereo = 0; - del_timer(&msp->wake_stereo); switch (msp->opmode) { case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ @@ -1807,12 +1703,10 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->volume, msp->balance); break; case OPMODE_SIMPLE: + case OPMODE_SIMPLER: /* the thread will do for us */ msp_wake_thread(client); break; - case OPMODE_SIMPLER: - msp34xxg_reset(client); - break; } break; @@ -1866,7 +1760,7 @@ 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; - msp_any_rescan(client); + msp_wake_thread(client); break; } @@ -1875,7 +1769,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); - msp_any_rescan(client); + msp_wake_thread(client); break; } @@ -1897,10 +1791,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; /* only set audmode */ - if (vt->audmode != -1 && - vt->audmode != 0) { + if (vt->audmode != -1 && vt->audmode != 0) msp_any_set_audmode(client, vt->audmode); - } break; } diff --git a/linux/drivers/media/video/msp3400-old.c b/linux/drivers/media/video/msp3400-old.c new file mode 100644 index 000000000..c0f090a91 --- /dev/null +++ b/linux/drivers/media/video/msp3400-old.c @@ -0,0 +1,1945 @@ +/* + * programming the msp34* sound processor family + * + * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> + * + * what works and what doesn't: + * + * AM-Mono + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat <fcrozat@mail.dotcom.fr> + * + * FM-Mono + * should work. The stereo modes are backward compatible to FM-mono, + * therefore FM-Mono should be allways available. + * + * FM-Stereo (B/G, used in germany) + * should work, with autodetect + * + * FM-Stereo (satellite) + * should work, no autodetect (i.e. default is mono, but you can + * switch to stereo -- untested) + * + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) + * should work, with autodetect. Support for NICAM was added by + * Pekka Pietikainen <pp@netppl.fi> + * + * + * TODO: + * - better SAT support + * + * + * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) + * using soundcore instead of OSS + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/videodev.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <asm/semaphore.h> +#include <asm/pgtable.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#include "audiochip.h" +#include "i2c-compat.h" +#include "id.h" +#else +#include <media/audiochip.h> +#include <media/id.h> +#endif +#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 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 */ + +struct msp3400c { + int rev1,rev2; + + int opmode; + int mode; + int norm; + int nicam_on; + int acb; + int main, second; /* sound carrier */ + int input; + int source; /* see msp34xxg_set_source */ + + /* v4l2 */ + int audmode; + int rxsubchans; + + int muted; + int volume, balance; + int bass, treble; + + /* thread */ + pid_t tpid; + struct completion texit; + wait_queue_head_t wq; + + int active:1; + int restart:1; + int rmmod:1; + + int watch_stereo; + struct timer_list wake_stereo; +}; + +#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 */ + +/* ---------------------------------------------------------------------- */ + +#define dprintk if (debug >= 1) printk +#define d2printk if (debug >= 2) printk + +MODULE_PARM(once,"i"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); +MODULE_PARM(debug,"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"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("Dual BSD/GPL"); /* FreeBSD uses this too */ + +/* ---------------------------------------------------------------------- */ + +#define I2C_MSP3400C 0x80 +#define I2C_MSP3400C_ALT 0x88 + +#define I2C_MSP3400C_DEM 0x10 +#define I2C_MSP3400C_DFP 0x12 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + I2C_MSP3400C >> 1, + I2C_MSP3400C_ALT >> 1, + I2C_CLIENT_END +}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END}; +I2C_CLIENT_INSMOD; + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the MSP3400C Sound processor */ + +#ifndef I2C_M_IGNORE_NAK +# define I2C_M_IGNORE_NAK 0x1000 +#endif + +static int msp3400c_reset(struct i2c_client *client) +{ + /* reset and read revision code */ + static char reset_off[3] = { 0x00, 0x80, 0x00 }; + static char reset_on[3] = { 0x00, 0x00, 0x00 }; + static char write[3] = { I2C_MSP3400C_DFP + 1, 0x00, 0x1e }; + char read[2]; + struct i2c_msg reset[2] = { + { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, + { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, + }; + struct i2c_msg test[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read }, + }; + + if ( (1 != i2c_transfer(client->adapter,&reset[0],1)) || + (1 != i2c_transfer(client->adapter,&reset[1],1)) || + (2 != i2c_transfer(client->adapter,test,2)) ) { + printk(KERN_ERR "msp3400: chip reset failed\n"); + return -1; + } + return 0; +} + +static int +msp3400c_read(struct i2c_client *client, int dev, int addr) +{ + int err; + + unsigned char write[3]; + unsigned char read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + write[0] = dev+1; + write[1] = addr >> 8; + write[2] = addr & 0xff; + + for (err = 0; err < 3;) { + if (2 == i2c_transfer(client->adapter,msgs,2)) + break; + err++; + printk(KERN_WARNING "msp34xx: I/O error #%d (read 0x%02x/0x%02x)\n", + err, dev, addr); + msleep(10); + } + if (3 == err) { + printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); + msp3400c_reset(client); + return -1; + } + return read[0] << 8 | read[1]; +} + +static int +msp3400c_write(struct i2c_client *client, int dev, int addr, int val) +{ + int err; + unsigned char buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + + for (err = 0; err < 3;) { + if (5 == i2c_master_send(client, buffer, 5)) + break; + err++; + printk(KERN_WARNING "msp34xx: I/O error #%d (write 0x%02x/0x%02x)\n", + err, dev, addr); + msleep(10); + } + if (3 == err) { + printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); + msp3400c_reset(client); + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) + +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 +#define MSP_MODE_EXTERN 9 + +static struct MSP_INIT_DATA_DEM { + int fir1[6]; + int fir2[6]; + int cdo1; + int cdo2; + int ad_cv; + int mode_reg; + int dfp_src; + int dfp_matrix; +} msp_init_data[] = { + /* AM (for carrier detect / msp3400) */ + { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0500, 0x0020, 0x3000}, + + /* AM (for carrier detect / msp3410) */ + { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0100, 0x0020, 0x3000}, + + /* FM Radio */ + { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), + 0x00d0, 0x0480, 0x0020, 0x3000 }, + + /* Terrestial FM-mono + FM-stereo */ + { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0480, 0x0030, 0x3000}, + + /* Sat FM-mono */ + { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0480, 0x0000, 0x3000}, + + /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ + { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM/FM -- I (6.0/6.552) */ + { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(6.0), MSP_CARRIER(6.0), + 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM/AM -- L (6.5/5.85) */ + { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7c03}, +}; + +struct CARRIER_DETECT { + int cdo; + char *name; +}; + +static struct CARRIER_DETECT carrier_detect_main[] = { + /* main carrier */ + { MSP_CARRIER(4.5), "4.5 NTSC" }, + { MSP_CARRIER(5.5), "5.5 PAL B/G" }, + { MSP_CARRIER(6.0), "6.0 PAL I" }, + { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } +}; + +static struct CARRIER_DETECT carrier_detect_55[] = { + /* PAL B/G */ + { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, + { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } +}; + +static struct CARRIER_DETECT carrier_detect_65[] = { + /* PAL SAT / SECAM */ + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, + { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, + { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, + { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, + { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, + { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, +}; + +#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) + +/* ----------------------------------------------------------------------- */ + +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 }, + { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, + { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, +}; + +static char *scart_names[] = { + "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" +}; + +static void +msp3400c_set_scart(struct i2c_client *client, int in, int out) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + if (-1 == scarts[out][in]) + return; + + dprintk(KERN_DEBUG + "msp34xx: scart switch: %s => %d\n",scart_names[in],out); + msp->acb &= ~scarts[out][SCART_MASK]; + msp->acb |= scarts[out][in]; + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb); +} + +/* ------------------------------------------------------------------------ */ + +static void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) +{ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ +} + +static void msp3400c_setvolume(struct i2c_client *client, + int muted, int volume, int balance) +{ + int val = 0, bal = 0; + + if (!muted) { + val = (volume * 0x73 / 65535) << 8; + } + if (val) { + bal = (balance / 256) - 128; + } + dprintk(KERN_DEBUG + "msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", + 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 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, + muted ? 0x01 : (val | 0x01)); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, bal << 8); +} + +static void msp3400c_setbass(struct i2c_client *client, int bass) +{ + int val = ((bass-32768) * 0x60 / 65535) << 8; + + dprintk(KERN_DEBUG "msp34xx: setbass: %d 0x%02x\n",bass, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ +} + +static void msp3400c_settreble(struct i2c_client *client, int treble) +{ + int val = ((treble-32768) * 0x60 / 65535) << 8; + + dprintk(KERN_DEBUG "msp34xx: settreble: %d 0x%02x\n",treble, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ +} + +static void msp3400c_setmode(struct i2c_client *client, int type) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int i; + + dprintk(KERN_DEBUG "msp3400: setmode: %d\n",type); + msp->mode = type; + msp->audmode = V4L2_TUNER_MODE_MONO; + msp->rxsubchans = V4L2_TUNER_SUB_MONO; + + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ + msp_init_data[type].ad_cv); + + for (i = 5; i >= 0; i--) /* fir 1 */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, + msp_init_data[type].fir1[i]); + + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); + for (i = 5; i >= 0; i--) + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, + msp_init_data[type].fir2[i]); + + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ + msp_init_data[type].mode_reg); + + msp3400c_setcarrier(client, msp_init_data[type].cdo1, + msp_init_data[type].cdo2); + + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ + + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + 0x0520); /* I2S1 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + 0x0620); /* I2S2 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, + msp_init_data[type].dfp_src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + msp_init_data[type].dfp_src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + msp_init_data[type].dfp_src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, + msp_init_data[type].dfp_src); + } + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, + msp_init_data[type].dfp_src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, + msp_init_data[type].dfp_matrix); + + if (HAVE_NICAM(msp)) { + /* nicam prescale */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ + } +} + +static int best_audio_mode(int rxsubchans) +{ + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + return V4L2_TUNER_MODE_STEREO; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + return V4L2_TUNER_MODE_LANG1; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + return V4L2_TUNER_MODE_LANG2; + return V4L2_TUNER_MODE_MONO; +} + +/* turn on/off nicam + stereo */ +static void msp3400c_set_audmode(struct i2c_client *client, int audmode) +{ + static char *strmode[16] = { +#if __GNUC__ >= 3 + [ 0 ... 15 ] = "invalid", +#endif + [ V4L2_TUNER_MODE_MONO ] = "mono", + [ V4L2_TUNER_MODE_STEREO ] = "stereo", + [ V4L2_TUNER_MODE_LANG1 ] = "lang1", + [ V4L2_TUNER_MODE_LANG2 ] = "lang2", + }; + struct msp3400c *msp = i2c_get_clientdata(client); + int nicam=0; /* channel source: FM/AM or nicam */ + int src=0; + + BUG_ON(msp->opmode == OPMODE_SIMPLER); + msp->audmode = audmode; + + /* switch demodulator */ + switch (msp->mode) { + case MSP_MODE_FM_TERRA: + dprintk(KERN_DEBUG "msp3400: FM setstereo: %s\n", + strmode[audmode]); + msp3400c_setcarrier(client,msp->second,msp->main); + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); + break; + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); + break; + } + break; + case MSP_MODE_FM_SAT: + dprintk(KERN_DEBUG "msp3400: SAT setstereo: %s\n", + strmode[audmode]); + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + break; + case V4L2_TUNER_MODE_STEREO: + msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG1: + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG2: + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + } + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + dprintk(KERN_DEBUG "msp3400: NICAM setstereo: %s\n", + strmode[audmode]); + msp3400c_setcarrier(client,msp->second,msp->main); + if (msp->nicam_on) + nicam=0x0100; + break; + case MSP_MODE_BTSC: + dprintk(KERN_DEBUG "msp3400: BTSC setstereo: %s\n", + strmode[audmode]); + nicam=0x0300; + break; + case MSP_MODE_EXTERN: + dprintk(KERN_DEBUG "msp3400: extern setstereo: %s\n", + strmode[audmode]); + nicam = 0x0200; + break; + case MSP_MODE_FM_RADIO: + dprintk(KERN_DEBUG "msp3400: FM-Radio setstereo: %s\n", + strmode[audmode]); + break; + default: + dprintk(KERN_DEBUG "msp3400: mono setstereo\n"); + return; + } + + /* switch audio */ + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: + src = 0x0020 | nicam; +#if 0 + /* spatial effect */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); +#endif + break; + case V4L2_TUNER_MODE_MONO: + if (msp->mode == MSP_MODE_AM_NICAM) { + dprintk("msp3400: switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* SCART switching control register */ + msp3400c_set_scart(client,SCART_MONO,0); + src = 0x0200; + break; + } + case V4L2_TUNER_MODE_LANG1: + src = 0x0000 | nicam; + break; + case V4L2_TUNER_MODE_LANG2: + src = 0x0010 | nicam; + break; + } + dprintk(KERN_DEBUG + "msp3400: setstereo final source/matrix = 0x%x\n", src); + + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); + } +} + +static void +msp3400c_print_mode(struct msp3400c *msp) +{ + if (msp->main == msp->second) { + printk(KERN_DEBUG "msp3400: mono sound carrier: %d.%03d MHz\n", + msp->main/910000,(msp->main/910)%1000); + } else { + printk(KERN_DEBUG "msp3400: main sound carrier: %d.%03d MHz\n", + msp->main/910000,(msp->main/910)%1000); + } + if (msp->mode == MSP_MODE_FM_NICAM1 || + msp->mode == MSP_MODE_FM_NICAM2) + printk(KERN_DEBUG "msp3400: NICAM/FM carrier : %d.%03d MHz\n", + msp->second/910000,(msp->second/910)%1000); + if (msp->mode == MSP_MODE_AM_NICAM) + printk(KERN_DEBUG "msp3400: NICAM/AM carrier : %d.%03d MHz\n", + msp->second/910000,(msp->second/910)%1000); + if (msp->mode == MSP_MODE_FM_TERRA && + msp->main != msp->second) { + printk(KERN_DEBUG "msp3400: FM-stereo carrier : %d.%03d MHz\n", + msp->second/910000,(msp->second/910)%1000); + } +} + +/* ----------------------------------------------------------------------- */ + +struct REGISTER_DUMP { + int addr; + char *name; +}; + +struct REGISTER_DUMP d1[] = { + { 0x007e, "autodetect" }, + { 0x0023, "C_AD_BITS " }, + { 0x0038, "ADD_BITS " }, + { 0x003e, "CIB_BITS " }, + { 0x0057, "ERROR_RATE" }, +}; + +static int +autodetect_stereo(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + int val; + int rxsubchans = msp->rxsubchans; + int newnicam = msp->nicam_on; + int update = 0; + + switch (msp->mode) { + case MSP_MODE_FM_TERRA: + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); + if (val > 32767) + val -= 65536; + dprintk(KERN_DEBUG + "msp34xx: stereo detect register: %d\n",val); + if (val > 4096) { + rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + } else if (val < -4096) { + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + rxsubchans = V4L2_TUNER_SUB_MONO; + } + newnicam = 0; + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); + dprintk(KERN_DEBUG + "msp34xx: nicam sync=%d, mode=%d\n", + val & 1, (val & 0x1e) >> 1); + + if (val & 1) { + /* nicam synced */ + switch ((val & 0x1e) >> 1) { + case 0: + case 8: + rxsubchans = V4L2_TUNER_SUB_STEREO; + break; + case 1: + case 9: + rxsubchans = V4L2_TUNER_SUB_MONO + | V4L2_TUNER_SUB_LANG1; + break; + case 2: + case 10: + rxsubchans = V4L2_TUNER_SUB_MONO + | V4L2_TUNER_SUB_LANG1 + | V4L2_TUNER_SUB_LANG2; + break; + default: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + } + newnicam=1; + } else { + newnicam = 0; + rxsubchans = V4L2_TUNER_SUB_MONO; + } + break; + case MSP_MODE_BTSC: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); + dprintk(KERN_DEBUG + "msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + val, + (val & 0x0002) ? "no" : "yes", + (val & 0x0004) ? "no" : "yes", + (val & 0x0040) ? "stereo" : "mono", + (val & 0x0080) ? ", nicam 2nd mono" : "", + (val & 0x0100) ? ", bilingual/SAP" : ""); + rxsubchans = V4L2_TUNER_SUB_MONO; + if (val & 0x0040) rxsubchans |= V4L2_TUNER_SUB_STEREO; + if (val & 0x0100) rxsubchans |= V4L2_TUNER_SUB_LANG1; + break; + } + if (rxsubchans != msp->rxsubchans) { + update = 1; + dprintk(KERN_DEBUG "msp34xx: watch: rxsubchans %d => %d\n", + msp->rxsubchans,rxsubchans); + msp->rxsubchans = rxsubchans; + } + if (newnicam != msp->nicam_on) { + update = 1; + dprintk(KERN_DEBUG "msp34xx: watch: nicam %d => %d\n", + msp->nicam_on,newnicam); + msp->nicam_on = newnicam; + } + return update; +} + +/* + * A kernel thread for msp3400 control -- we don't want to block the + * in the ioctl while doing the sound carrier & stereo detect + */ + +static int msp34xx_sleep(struct msp3400c *msp, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&msp->wq, &wait); + if (!msp->rmmod) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else + msleep_interruptible(timeout); + } + remove_wait_queue(&msp->wq, &wait); + return msp->rmmod || signal_pending(current); +} + +static void msp3400c_stereo_wake(unsigned long data) +{ + struct msp3400c *msp = (struct msp3400c*)data; /* XXX alpha ??? */ + + wake_up_interruptible(&msp->wq); +} + +/* stereo/multilang monitoring */ +static void watch_stereo(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + if (autodetect_stereo(client)) + msp3400c_set_audmode(client,best_audio_mode(msp->rxsubchans)); + if (once) + msp->watch_stereo = 0; + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+5*HZ); +} + +static int msp3400c_thread(void *data) +{ + struct i2c_client *client = data; + struct msp3400c *msp = i2c_get_clientdata(client); + + struct CARRIER_DETECT *cd; + int count, max1,max2,val1,val2, val,this; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + lock_kernel(); + daemonize(); + sigfillset(¤t->blocked); + strcpy(current->comm,"msp3400"); + unlock_kernel(); +#else + daemonize("msp3400"); + allow_signal(SIGTERM); +#endif + printk("msp3400: daemon started\n"); + + for (;;) { + d2printk("msp3400: thread: sleep\n"); + if (msp34xx_sleep(msp,-1)) + goto done; + + d2printk("msp3400: thread: wakeup\n"); + msp->active = 1; + + if (msp->watch_stereo) { + watch_stereo(client); + msp->active = 0; + continue; + } + + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto done; + + restart: + if (VIDEO_MODE_RADIO == msp->norm || + MSP_MODE_EXTERN == msp->mode) { + /* no carrier scan, just unmute */ + printk("msp3400: thread: no carrier scan\n"); + msp3400c_setvolume(client, msp->muted, + msp->volume, msp->balance); + continue; + } + msp->restart = 0; + msp3400c_setvolume(client, msp->muted, 0, 0); + msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); + val1 = val2 = 0; + max1 = max2 = -1; + del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; + + /* carrier detect pass #1 -- main carrier */ + cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + dprintk("msp3400: AM sound override\n"); + } + + for (this = 0; this < count; this++) { + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); + + if (msp34xx_sleep(msp,100)) + goto done; + if (msp->restart) + msp->restart = 0; + + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); + if (val > 32767) + val -= 65536; + if (val1 < val) + val1 = val, max1 = this; + dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); + } + + /* carrier detect pass #2 -- second (stereo) carrier */ + switch (max1) { + case 1: /* 5.5 */ + 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); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; count = 0; + break; + } + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; count = 0; max2 = 0; + } + for (this = 0; this < count; this++) { + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); + + if (msp34xx_sleep(msp,100)) + goto done; + if (msp->restart) + goto restart; + + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); + if (val > 32767) + val -= 65536; + if (val2 < val) + val2 = val, max2 = this; + dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); + } + + /* programm the msp3400 according to the results */ + msp->main = carrier_detect_main[max1].cdo; + switch (max1) { + case 1: /* 5.5 */ + if (max2 == 0) { + /* B/G FM-stereo */ + msp->second = carrier_detect_55[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + msp->nicam_on = 0; + msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO); + msp->watch_stereo = 1; + } else if (max2 == 1 && HAVE_NICAM(msp)) { + /* B/G NICAM */ + msp->second = carrier_detect_55[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); + msp->nicam_on = 1; + msp3400c_setcarrier(client, msp->second, msp->main); + msp->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 2: /* 6.0 */ + /* PAL I NICAM */ + msp->second = MSP_CARRIER(6.552); + msp3400c_setmode(client, MSP_MODE_FM_NICAM2); + msp->nicam_on = 1; + msp3400c_setcarrier(client, msp->second, msp->main); + msp->watch_stereo = 1; + break; + case 3: /* 6.5 */ + if (max2 == 1 || max2 == 2) { + /* D/K FM-stereo */ + msp->second = carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + msp->nicam_on = 0; + msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO); + msp->watch_stereo = 1; + } else if (max2 == 0 && + msp->norm == VIDEO_MODE_SECAM) { + /* L NICAM or AM-mono */ + msp->second = carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_AM_NICAM); + msp->nicam_on = 0; + msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO); + msp3400c_setcarrier(client, msp->second, msp->main); + /* volume prescale for SCART (AM mono input) */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); + msp->watch_stereo = 1; + } else if (max2 == 0 && HAVE_NICAM(msp)) { + /* D/K NICAM */ + msp->second = carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); + msp->nicam_on = 1; + msp3400c_setcarrier(client, msp->second, msp->main); + msp->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 0: /* 4.5 */ + default: + no_second: + msp->second = carrier_detect_main[max1].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + msp->nicam_on = 0; + msp3400c_setcarrier(client, msp->second, msp->main); + msp->rxsubchans = V4L2_TUNER_SUB_MONO; + msp3400c_set_audmode(client, V4L2_TUNER_MODE_MONO); + break; + } + + /* unmute + restore dfp registers */ + msp3400c_setvolume(client, msp->muted, + msp->volume, msp->balance); + + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+5*HZ); + + if (debug) + msp3400c_print_mode(msp); + + msp->active = 0; + } + +done: + msp->active = 0; + dprintk(KERN_DEBUG "msp3400: thread: exit\n"); + complete_and_exit(&msp->texit, 0); +} + +/* ----------------------------------------------------------------------- */ +/* this one uses the automatic sound standard detection of newer */ +/* msp34xx chip versions */ + +static struct MODES { + int retval; + int main, second; + char *name; +} modelist[] = { + { 0x0000, 0, 0, "ERROR" }, + { 0x0001, 0, 0, "autodetect start" }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, + { 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) +{ + struct i2c_client *client = data; + struct msp3400c *msp = i2c_get_clientdata(client); + int mode,val,i,std; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + lock_kernel(); + daemonize(); + sigfillset(¤t->blocked); + strcpy(current->comm,"msp3410 [auto]"); + unlock_kernel(); +#else + daemonize("msp3410 [auto]"); + allow_signal(SIGTERM); +#endif + printk("msp3410: daemon started\n"); + + for (;;) { + d2printk(KERN_DEBUG "msp3410: thread: sleep\n"); + if (msp34xx_sleep(msp,-1)) + goto done; + + d2printk(KERN_DEBUG "msp3410: thread: wakeup\n"); + msp->active = 1; + + if (msp->watch_stereo) { + watch_stereo(client); + msp->active = 0; + continue; + } + + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto done; + + restart: + if (msp->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + dprintk(KERN_DEBUG "msp3410: thread: no carrier scan\n"); + msp3400c_setvolume(client, msp->muted, + msp->volume, msp->balance); + continue; + } + msp->restart = 0; + del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; + + /* put into sane state (and mute) */ + msp3400c_reset(client); + + /* start autodetect */ + 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) + printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", + msp34xx_standard_mode_name(std) ,std); + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + for (;;) { + if (msp34xx_sleep(msp,100)) + goto done; + if (msp->restart) + goto restart; + + /* check results */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); + if (val < 0x07ff) + break; + dprintk(KERN_DEBUG "msp3410: detection still in progress\n"); + } + } + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == val) + break; + dprintk(KERN_DEBUG "msp3410: current mode: %s (0x%04x)\n", + modelist[i].name ? modelist[i].name : "unknown", + val); + msp->main = modelist[i].main; + msp->second = modelist[i].second; + + if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { + /* autodetection has failed, let backup */ + dprintk(KERN_DEBUG "msp3410: autodetection failed," + " switching to backup mode: %s (0x%04x)\n", + modelist[8].name ? modelist[8].name : "unknown",val); + val = 0x0009; + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); + } + + /* set various prescales */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ + + /* set stereo */ + switch (val) { + case 0x0008: /* B/G NICAM */ + case 0x000a: /* I NICAM */ + if (val == 0x0008) + msp->mode = MSP_MODE_FM_NICAM1; + else + msp->mode = MSP_MODE_FM_NICAM2; + /* just turn on stereo */ + msp->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp->nicam_on = 1; + msp->watch_stereo = 1; + msp3400c_set_audmode(client,V4L2_TUNER_MODE_STEREO); + break; + case 0x0009: + msp->mode = MSP_MODE_AM_NICAM; + msp->rxsubchans = V4L2_TUNER_SUB_MONO; + msp->nicam_on = 1; + msp3400c_set_audmode(client,V4L2_TUNER_MODE_MONO); + msp->watch_stereo = 1; + break; + case 0x0020: /* BTSC */ + /* just turn on stereo */ + msp->mode = MSP_MODE_BTSC; + msp->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp->nicam_on = 0; + msp->watch_stereo = 1; + msp3400c_set_audmode(client,V4L2_TUNER_MODE_STEREO); + break; + case 0x0040: /* FM radio */ + msp->mode = MSP_MODE_FM_RADIO; + msp->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp->audmode = V4L2_TUNER_MODE_STEREO; + msp->nicam_on = 0; + msp->watch_stereo = 0; + /* not needed in theory if HAVE_RADIO(), but + short programming enables carrier mute */ + msp3400c_setmode(client,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + /* scart routing */ + msp3400c_set_scart(client,SCART_IN2,0); +#if 0 + /* radio from SCART_IN2 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); +#else + /* msp34xx does radio decoding */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0020); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0020); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0020); +#endif + break; + case 0x0003: + case 0x0004: + case 0x0005: + msp->mode = MSP_MODE_FM_TERRA; + msp->rxsubchans = V4L2_TUNER_SUB_MONO; + msp->audmode = V4L2_TUNER_MODE_MONO; + msp->nicam_on = 0; + msp->watch_stereo = 1; + break; + } + + /* 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); + + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+HZ); + + msp->active = 0; + } + +done: + msp->active = 0; + dprintk(KERN_DEBUG "msp3410: thread: exit\n"); + complete_and_exit(&msp->texit, 0); + return 0; +} + +/* ----------------------------------------------------------------------- */ +/* 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(client, I2C_MSP3400C_DFP, + 0x0e, /* AM/FM Prescale */ + 0x3000 /* default: [15:8] 75khz deviation */)) + return -1; + + if (msp3400c_write(client, I2C_MSP3400C_DFP, + 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 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; +} + +static void msp34xxg_detect_stereo(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + int status = msp3400c_read(client, + I2C_MSP3400C_DEM, + 0x0200 /* STATUS */); + int is_bilingual = status&0x100; + int is_stereo = status&0x40; + + msp->rxsubchans = 0; + if (is_stereo) + msp->rxsubchans |= V4L2_TUNER_SUB_STEREO; + else + msp->rxsubchans |= V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + msp->rxsubchans |= 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, msp->rxsubchans); +} + +static void msp34xxg_set_audmode(struct i2c_client *client, int audmode) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + 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 */ + audmode = 0; + return; + } + msp->audmode = audmode; + msp34xxg_set_source(client, source); +} + + +/* ----------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +static int msp_attach(struct i2c_adapter *adap, int addr, int kind); +#else +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind); +#endif +static int msp_detach(struct i2c_client *client); +static int msp_probe(struct i2c_adapter *adap); +static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static struct i2c_driver driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + .owner = THIS_MODULE, +#endif + .name = "i2c msp3400 driver", + .id = I2C_DRIVERID_MSP3400, + .flags = I2C_DF_NOTIFY, + .attach_adapter = msp_probe, + .detach_client = msp_detach, + .command = msp_command, +}; + +static struct i2c_client client_template = +{ + I2C_DEVNAME("(unset)"), + .flags = I2C_CLIENT_ALLOW_USE, + .driver = &driver, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +static int msp_attach(struct i2c_adapter *adap, int addr, int kind) +#else +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +#endif +{ + struct msp3400c *msp; + struct i2c_client *c; + int (*thread_func)(void *data) = NULL; + + client_template.adapter = adap; + client_template.addr = addr; + + if (-1 == msp3400c_reset(&client_template)) { + dprintk("msp3400: no chip found\n"); + return -1; + } + + if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(c,&client_template,sizeof(struct i2c_client)); + if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { + kfree(c); + return -ENOMEM; + } + + memset(msp,0,sizeof(struct msp3400c)); + msp->volume = 58880; /* 0db gain */ + msp->balance = 32768; + msp->bass = 32768; + msp->treble = 32768; + msp->input = -1; + msp->muted = 1; + + i2c_set_clientdata(c, msp); + init_waitqueue_head(&msp->wq); + + if (-1 == msp3400c_reset(c)) { + kfree(msp); + kfree(c); + dprintk("msp3400: no chip found\n"); + return -1; + } + + msp->rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); + if (-1 != msp->rev1) + msp->rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); + if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) { + kfree(msp); + kfree(c); + printk("msp3400: error while reading chip version\n"); + return -1; + } + +#if 0 + /* this will turn on a 1kHz beep - might be useful for debugging... */ + msp3400c_write(c,I2C_MSP3400C_DFP, 0x0014, 0x1040); +#endif + msp3400c_setvolume(c, msp->muted, msp->volume, msp->balance); + + snprintf(c->name, sizeof(c->name), "MSP34%02d%c-%c%d", + (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@', + ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); + + 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 */ + init_timer(&msp->wake_stereo); + msp->wake_stereo.function = msp3400c_stereo_wake; + msp->wake_stereo.data = (unsigned long)msp; + + /* hello world :-) */ + printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c)); + if (HAVE_NICAM(msp)) + printk(" +nicam"); + if (HAVE_SIMPLE(msp)) + printk(" +simple"); + if (HAVE_SIMPLER(msp)) + printk(" +simpler"); + if (HAVE_RADIO(msp)) + printk(" +radio"); + + /* version-specific initialization */ + switch (msp->opmode) { + case OPMODE_MANUAL: + printk(" mode=manual"); + thread_func = msp3400c_thread; + break; + case OPMODE_SIMPLE: + printk(" mode=simple"); + thread_func = msp3410d_thread; + break; + case OPMODE_SIMPLER: + printk(" mode=simpler"); + msp34xxg_reset(c); + break; + } + printk("\n"); + + /* 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 + i2c_attach_client(c); + return 0; +} + +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) { + msp->rmmod = 1; + wake_up_interruptible(&msp->wq); + wait_for_completion(&msp->texit); + } + msp3400c_reset(client); + + i2c_detach_client(client); + kfree(msp); + kfree(client); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int msp_probe(struct i2c_adapter *adap) +{ +#ifdef I2C_CLASS_TV_ANALOG + if (adap->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, msp_attach); +#else + switch (adap->id) { + case I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3: + case I2C_ALGO_BIT | I2C_HW_B_BT848: + //case I2C_ALGO_SAA7134: + return i2c_probe(adap, &addr_data, msp_attach); + break; + } +#endif + return 0; +} + +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); + if (msp->active) + msp->restart = 1; + wake_up_interruptible(&msp->wq); +} + +/* ----------------------------------------------------------------------- */ + +static int mode_v4l2_to_v4l1(int rxsubchans) +{ + int mode = 0; + + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + mode |= VIDEO_SOUND_STEREO; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + mode |= VIDEO_SOUND_LANG2; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + mode |= VIDEO_SOUND_LANG1; + if (0 == mode) + mode |= VIDEO_SOUND_MONO; + return mode; +} + +static int mode_v4l1_to_v4l2(int mode) +{ + if (mode & VIDEO_SOUND_STEREO) + return V4L2_TUNER_MODE_STEREO; + if (mode & VIDEO_SOUND_LANG2) + return V4L2_TUNER_MODE_LANG2; + if (mode & VIDEO_SOUND_LANG1) + return V4L2_TUNER_MODE_LANG1; + return V4L2_TUNER_MODE_MONO; +} + +static void msp_any_rescan(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } +} + +static void msp_any_detect_stereo(struct i2c_client *client) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + autodetect_stereo(client); + break; + case OPMODE_SIMPLER: + msp34xxg_detect_stereo(client); + break; + } +} + +static void msp_any_set_audmode(struct i2c_client *client, int audmode) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + + switch (msp->opmode) { + case OPMODE_MANUAL: + case OPMODE_SIMPLE: + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + msp3400c_set_audmode(client, audmode); + break; + case OPMODE_SIMPLER: + msp34xxg_set_audmode(client, audmode); + break; + } +} + +static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct msp3400c *msp = i2c_get_clientdata(client); + __u16 *sarg = arg; + int scart = 0; + + switch (cmd) { + + case AUDC_SET_INPUT: + dprintk(KERN_DEBUG "msp34xx: AUDC_SET_INPUT(%d)\n",*sarg); + if (*sarg == msp->input) + break; + msp->input = *sarg; + switch (*sarg) { + case AUDIO_RADIO: + /* Hauppauge uses IN2 for the radio */ + msp->mode = MSP_MODE_FM_RADIO; + scart = SCART_IN2; + break; + case AUDIO_EXTERN_1: + /* IN1 is often used for external input ... */ + msp->mode = MSP_MODE_EXTERN; + scart = SCART_IN1; + break; + case AUDIO_EXTERN_2: + /* ... sometimes it is IN2 through ;) */ + msp->mode = MSP_MODE_EXTERN; + scart = SCART_IN2; + break; + case AUDIO_TUNER: + msp->mode = -1; + msp_wake_thread(client); + break; + default: + if (*sarg & AUDIO_MUTE) + msp3400c_set_scart(client,SCART_MUTE,0); + break; + } + if (scart) { + msp->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp->audmode = V4L2_TUNER_MODE_STEREO; + msp3400c_set_scart(client,scart,0); + msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); + if (msp->opmode != OPMODE_SIMPLER) + msp3400c_set_audmode(client, msp->audmode); + } + if (msp->active) + msp->restart = 1; + break; + + 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); + 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); + break; + case OPMODE_SIMPLE: + /* the thread will do for us */ + msp_wake_thread(client); + break; + case OPMODE_SIMPLER: + msp34xxg_reset(client); + break; + } + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n"); + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE | + VIDEO_AUDIO_MUTABLE; + if (msp->muted) + va->flags |= VIDEO_AUDIO_MUTE; + + va->volume = msp->volume; + va->balance = (va->volume) ? msp->balance : 32768; + va->bass = msp->bass; + va->treble = msp->treble; + + msp_any_detect_stereo(client); + va->mode = mode_v4l2_to_v4l1(msp->rxsubchans); + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); + msp->muted = (va->flags & VIDEO_AUDIO_MUTE); + msp->volume = va->volume; + msp->balance = va->balance; + msp->bass = va->bass; + msp->treble = va->treble; + + msp3400c_setvolume(client, msp->muted, + msp->volume, msp->balance); + msp3400c_setbass(client,msp->bass); + msp3400c_settreble(client,msp->treble); + + if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) + msp_any_set_audmode(client,mode_v4l1_to_v4l2(va->mode)); + break; + } + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); + msp->norm = vc->norm; + msp_any_rescan(client); + break; + } + + case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: + { + /* new channel -- kick audio carrier scan */ + dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); + msp_any_rescan(client); + break; + } + + /* --- v4l2 ioctls --- */ + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + + msp_any_detect_stereo(client); + vt->audmode = msp->audmode; + vt->rxsubchans = msp->rxsubchans; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1| + V4L2_TUNER_CAP_LANG2; + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + + /* only set audmode */ + if (vt->audmode != -1 && + vt->audmode != 0) { + msp_any_set_audmode(client, vt->audmode); + } + break; + } + + /* msp34xx specific */ + 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; + } + + default: + /* nothing */ + break; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int __init msp3400_init_module(void) +{ + return i2c_add_driver(&driver); +} + +static void __exit msp3400_cleanup_module(void) +{ + i2c_del_driver(&driver); +} + +module_init(msp3400_init_module); +module_exit(msp3400_cleanup_module); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/media/video/msp3400.c b/linux/drivers/media/video/msp3400.c index c0f090a91..61b6fdcae 100644 --- a/linux/drivers/media/video/msp3400.c +++ b/linux/drivers/media/video/msp3400.c @@ -47,17 +47,12 @@ #include <linux/videodev.h> #include <linux/init.h> #include <linux/smp_lock.h> +#include <linux/kthread.h> #include <asm/semaphore.h> #include <asm/pgtable.h> -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -#include "audiochip.h" -#include "i2c-compat.h" -#include "id.h" -#else #include <media/audiochip.h> #include <media/id.h> -#endif #include "msp3400.h" #include "compat.h" @@ -99,16 +94,10 @@ struct msp3400c { int bass, treble; /* thread */ - pid_t tpid; - struct completion texit; + struct task_struct *kthread; wait_queue_head_t wq; - - int active:1; int restart:1; - int rmmod:1; - - int watch_stereo; - struct timer_list wake_stereo; + int watch_stereo:1; }; #define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) @@ -159,10 +148,6 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ -#ifndef I2C_M_IGNORE_NAK -# define I2C_M_IGNORE_NAK 0x1000 -#endif - static int msp3400c_reset(struct i2c_client *client) { /* reset and read revision code */ @@ -753,22 +738,22 @@ static int msp34xx_sleep(struct msp3400c *msp, int timeout) DECLARE_WAITQUEUE(wait, current); add_wait_queue(&msp->wq, &wait); - if (!msp->rmmod) { + if (!kthread_should_stop()) { if (timeout < 0) { set_current_state(TASK_INTERRUPTIBLE); schedule(); - } else + } else { +#if 0 + /* hmm, that one doesn't return after waking up msp->wq */ msleep_interruptible(timeout); +#else + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(timeout)); +#endif + } } remove_wait_queue(&msp->wq, &wait); - return msp->rmmod || signal_pending(current); -} - -static void msp3400c_stereo_wake(unsigned long data) -{ - struct msp3400c *msp = (struct msp3400c*)data; /* XXX alpha ??? */ - - wake_up_interruptible(&msp->wq); + return msp->restart; } /* stereo/multilang monitoring */ @@ -780,49 +765,27 @@ static void watch_stereo(struct i2c_client *client) msp3400c_set_audmode(client,best_audio_mode(msp->rxsubchans)); if (once) msp->watch_stereo = 0; - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+5*HZ); } static int msp3400c_thread(void *data) { struct i2c_client *client = data; struct msp3400c *msp = i2c_get_clientdata(client); - struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) - lock_kernel(); - daemonize(); - sigfillset(¤t->blocked); - strcpy(current->comm,"msp3400"); - unlock_kernel(); -#else - daemonize("msp3400"); - allow_signal(SIGTERM); -#endif - printk("msp3400: daemon started\n"); - + printk("msp3400: kthread started\n"); for (;;) { d2printk("msp3400: thread: sleep\n"); - if (msp34xx_sleep(msp,-1)) - goto done; - + msp34xx_sleep(msp,-1); d2printk("msp3400: thread: wakeup\n"); - msp->active = 1; - if (msp->watch_stereo) { - watch_stereo(client); - msp->active = 0; - continue; - } - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto done; - restart: + dprintk("msp3410: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; + if (VIDEO_MODE_RADIO == msp->norm || MSP_MODE_EXTERN == msp->mode) { /* no carrier scan, just unmute */ @@ -831,14 +794,18 @@ static int msp3400c_thread(void *data) msp->volume, msp->balance); continue; } - msp->restart = 0; + + /* mute */ msp3400c_setvolume(client, msp->muted, 0, 0); msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); val1 = val2 = 0; max1 = max2 = -1; - del_timer(&msp->wake_stereo); msp->watch_stereo = 0; + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto restart; + /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); @@ -851,12 +818,8 @@ static int msp3400c_thread(void *data) for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) - msp->restart = 0; - + goto restart; val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val > 32767) val -= 65536; @@ -888,12 +851,8 @@ static int msp3400c_thread(void *data) } for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) goto restart; - val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val > 32767) val -= 65536; @@ -974,23 +933,21 @@ static int msp3400c_thread(void *data) break; } - /* unmute + restore dfp registers */ + /* unmute */ msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); - - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+5*HZ); - if (debug) msp3400c_print_mode(msp); - - msp->active = 0; - } -done: - msp->active = 0; + /* monitor tv audio mode */ + while (msp->watch_stereo) { + if (msp34xx_sleep(msp,5000)) + goto restart; + watch_stereo(client); + } + } dprintk(KERN_DEBUG "msp3400: thread: exit\n"); - complete_and_exit(&msp->texit, 0); + return 0; } /* ----------------------------------------------------------------------- */ @@ -1073,37 +1030,18 @@ static int msp3410d_thread(void *data) struct msp3400c *msp = i2c_get_clientdata(client); int mode,val,i,std; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) - lock_kernel(); - daemonize(); - sigfillset(¤t->blocked); - strcpy(current->comm,"msp3410 [auto]"); - unlock_kernel(); -#else - daemonize("msp3410 [auto]"); - allow_signal(SIGTERM); -#endif printk("msp3410: daemon started\n"); - for (;;) { d2printk(KERN_DEBUG "msp3410: thread: sleep\n"); - if (msp34xx_sleep(msp,-1)) - goto done; - + msp34xx_sleep(msp,-1); d2printk(KERN_DEBUG "msp3410: thread: wakeup\n"); - msp->active = 1; - - if (msp->watch_stereo) { - watch_stereo(client); - msp->active = 0; - continue; - } - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto done; restart: + dprintk("msp3410: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; + if (msp->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ dprintk(KERN_DEBUG "msp3410: thread: no carrier scan\n"); @@ -1111,18 +1049,20 @@ static int msp3410d_thread(void *data) msp->volume, msp->balance); continue; } - msp->restart = 0; - del_timer(&msp->wake_stereo); - msp->watch_stereo = 0; /* put into sane state (and mute) */ msp3400c_reset(client); + /* some time for the tuner to sync */ + if (msp34xx_sleep(msp,200)) + goto restart; + /* start autodetect */ 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); + msp->watch_stereo = 0; if (debug) printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", @@ -1135,8 +1075,6 @@ static int msp3410d_thread(void *data) /* triggered autodetect */ for (;;) { if (msp34xx_sleep(msp,100)) - goto done; - if (msp->restart) goto restart; /* check results */ @@ -1241,16 +1179,14 @@ static int msp3410d_thread(void *data) msp->volume, msp->balance); msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); - if (msp->watch_stereo) - mod_timer(&msp->wake_stereo, jiffies+HZ); - - msp->active = 0; + /* monitor tv audio mode */ + while (msp->watch_stereo) { + if (msp34xx_sleep(msp,5000)) + goto restart; + watch_stereo(client); + } } - -done: - msp->active = 0; dprintk(KERN_DEBUG "msp3410: thread: exit\n"); - complete_and_exit(&msp->texit, 0); return 0; } @@ -1265,12 +1201,10 @@ 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) +static int msp34xxg_init(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - int i; int modus; - int std; if (msp3400c_reset(client)) return -1; @@ -1306,55 +1240,76 @@ static int msp34xxg_reset(struct i2c_client *client) 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*/)) + standard /* default: 0x01 for automatic standard select*/)) return -1; + return 0; +} + +static int msp34xxg_thread(void *data) +{ + struct i2c_client *client = data; + struct msp3400c *msp = i2c_get_clientdata(client); + int val, std, i; + + printk("msp34xxg: daemon started\n"); + for (;;) { + d2printk(KERN_DEBUG "msp34xxg: thread: sleep\n"); + msp34xx_sleep(msp,-1); + d2printk(KERN_DEBUG "msp34xxg: thread: wakeup\n"); + restart: + dprintk("msp34xxg: thread: restart scan\n"); + msp->restart = 0; + if (kthread_should_stop()) + break; - if (std == 0x01) { - dprintk("msp34xxg: triggered autodetect, waiting for result\n"); + /* setup the chip*/ + msp34xxg_init(client); + std = standard; + if (std != 0x01) + goto unmute; - /* triggered autodetect */ + /* watch autodetect */ + dprintk("msp34xxg: triggered autodetect, waiting for result\n"); for (i = 0; i < 10; i++) { - int val; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); - if (signal_pending(current)) - return -1; /* failed */ + if (msp34xx_sleep(msp,100)) + goto restart; /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) { - std=val; + std = val; break; } dprintk("msp34xxg: detection still in progress\n"); } - if (i == 10) { + if (0x01 == std) { dprintk("msp34xxg: detection still in progress after 10 tries. giving up.\n"); - return -1; + continue; } - } - 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 ACB */ - if (msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x13, /* ACB */ - msp->acb)) - return -1; + unmute: + 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 ACB */ + if (msp3400c_write(client, + I2C_MSP3400C_DFP, + 0x13, /* ACB */ + msp->acb)) + return -1; + } + dprintk(KERN_DEBUG "msp34xxg: thread: exit\n"); return 0; } @@ -1457,21 +1412,14 @@ static void msp34xxg_set_audmode(struct i2c_client *client, int audmode) /* ----------------------------------------------------------------------- */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind); -#else -static int msp_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind); -#endif static int msp_detach(struct i2c_client *client); static int msp_probe(struct i2c_adapter *adap); static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); static struct i2c_driver driver = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) .owner = THIS_MODULE, -#endif - .name = "i2c msp3400 driver", + .name = "i2c msp3400 driver", .id = I2C_DRIVERID_MSP3400, .flags = I2C_DF_NOTIFY, .attach_adapter = msp_probe, @@ -1486,12 +1434,7 @@ static struct i2c_client client_template = .driver = &driver, }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int msp_attach(struct i2c_adapter *adap, int addr, int kind) -#else -static int msp_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) -#endif { struct msp3400c *msp; struct i2c_client *c; @@ -1561,11 +1504,6 @@ static int msp_attach(struct i2c_adapter *adap, int addr, msp->opmode = OPMODE_MANUAL; } - /* timer for stereo checking */ - init_timer(&msp->wake_stereo); - msp->wake_stereo.function = msp3400c_stereo_wake; - msp->wake_stereo.data = (unsigned long)msp; - /* hello world :-) */ printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c)); if (HAVE_NICAM(msp)) @@ -1589,26 +1527,20 @@ static int msp_attach(struct i2c_adapter *adap, int addr, break; case OPMODE_SIMPLER: printk(" mode=simpler"); - msp34xxg_reset(c); + thread_func = msp34xxg_thread; break; } printk("\n"); /* 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) + msp->kthread = kthread_run(thread_func, c, "msp34xx"); + if (NULL == msp->kthread) 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 i2c_attach_client(c); return 0; } @@ -1618,37 +1550,22 @@ 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) { - msp->rmmod = 1; - wake_up_interruptible(&msp->wq); - wait_for_completion(&msp->texit); + if (msp->kthread >= 0) { + msp->restart = 1; + kthread_stop(msp->kthread); } msp3400c_reset(client); i2c_detach_client(client); kfree(msp); kfree(client); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#endif return 0; } static int msp_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, msp_attach); -#else - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3: - case I2C_ALGO_BIT | I2C_HW_B_BT848: - //case I2C_ALGO_SAA7134: - return i2c_probe(adap, &addr_data, msp_attach); - break; - } -#endif return 0; } @@ -1656,13 +1573,11 @@ static void msp_wake_thread(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); - if (-1 == msp->tpid) + if (NULL == msp->kthread) return; msp3400c_setvolume(client,msp->muted,0,0); - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); - if (msp->active) - msp->restart = 1; + msp->watch_stereo = 0; + msp->restart = 1; wake_up_interruptible(&msp->wq); } @@ -1694,21 +1609,6 @@ static int mode_v4l1_to_v4l2(int mode) return V4L2_TUNER_MODE_MONO; } -static void msp_any_rescan(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - switch (msp->opmode) { - case OPMODE_MANUAL: - case OPMODE_SIMPLE: - msp_wake_thread(client); - break; - case OPMODE_SIMPLER: - msp34xxg_reset(client); - break; - } -} - static void msp_any_detect_stereo(struct i2c_client *client) { struct msp3400c *msp = i2c_get_clientdata(client); @@ -1731,8 +1631,7 @@ static void msp_any_set_audmode(struct i2c_client *client, int audmode) switch (msp->opmode) { case OPMODE_MANUAL: case OPMODE_SIMPLE: - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; msp3400c_set_audmode(client, audmode); break; case OPMODE_SIMPLER: @@ -1772,7 +1671,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case AUDIO_TUNER: msp->mode = -1; - msp_wake_thread(client); break; default: if (*sarg & AUDIO_MUTE) @@ -1787,8 +1685,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (msp->opmode != OPMODE_SIMPLER) msp3400c_set_audmode(client, msp->audmode); } - if (msp->active) - msp->restart = 1; + msp_wake_thread(client); break; case AUDC_SET_RADIO: @@ -1796,7 +1693,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->norm = VIDEO_MODE_RADIO; dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); msp->watch_stereo = 0; - del_timer(&msp->wake_stereo); switch (msp->opmode) { case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ @@ -1807,12 +1703,10 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp->volume, msp->balance); break; case OPMODE_SIMPLE: + case OPMODE_SIMPLER: /* the thread will do for us */ msp_wake_thread(client); break; - case OPMODE_SIMPLER: - msp34xxg_reset(client); - break; } break; @@ -1866,7 +1760,7 @@ 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; - msp_any_rescan(client); + msp_wake_thread(client); break; } @@ -1875,7 +1769,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); - msp_any_rescan(client); + msp_wake_thread(client); break; } @@ -1897,10 +1791,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; /* only set audmode */ - if (vt->audmode != -1 && - vt->audmode != 0) { + if (vt->audmode != -1 && vt->audmode != 0) msp_any_set_audmode(client, vt->audmode); - } break; } diff --git a/v4l/scripts/update b/v4l/scripts/update index a79a46fe2..1da62378a 100644 --- a/v4l/scripts/update +++ b/v4l/scripts/update @@ -4,7 +4,7 @@ set -e case "`hostname --short`" in eskarina) -# btopt="card=10" + btopt="card=10" # btopt="card=6 remote=1" ;; bogomips) @@ -59,7 +59,7 @@ xinsmod btcx-risc debug=0 xinsmod ir-common debug=0 repeat=0 xinsmod tuner debug=0 xinsmod tda9887 debug=0 -xinsmod msp3400 debug=0 +xinsmod msp3400 debug=1 # bttv xinsmod bttv bttv_debug=0 vbi_debug=0 irq_debug=0 \ |