summaryrefslogtreecommitdiffstats
path: root/drivers/char/msp3400.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/msp3400.c')
-rw-r--r--drivers/char/msp3400.c414
1 files changed, 279 insertions, 135 deletions
diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c
index 651d5b733..abf08764d 100644
--- a/drivers/char/msp3400.c
+++ b/drivers/char/msp3400.c
@@ -1,21 +1,21 @@
/*
* programming the msp34* sound processor family
*
- * (c) 1997,1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+ * (c) 1997,1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
*
* what works and what doesn't:
*
- * AM mono
+ * AM-Mono
* probably doesn't (untested)
*
- * FM-mono
- * should work. FM stereo modes are backward-compatible to mono.
- * Therefore FM mono should always be available.
+ * 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)
+ * FM-Stereo (B/G, used in germany)
* should work, with autodetect
*
- * FM stereo (satellite)
+ * FM-Stereo (satellite)
* should work, no autodetect (i.e. default is mono, but you can
* switch to stereo -- untested)
*
@@ -27,9 +27,14 @@
* TODO:
* - better SAT support
*
+ *
+ * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * using soundcore instead of OSS
+ *
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -37,6 +42,10 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/malloc.h>
+#ifdef __SMP__
+#include <asm/pgtable.h>
+#include <linux/smp_lock.h>
+#endif
/* kernel_thread */
#define __KERNEL_SYSCALLS__
@@ -67,14 +76,14 @@ struct msp3400c {
int mode;
int norm;
int stereo;
+ int main, second; /* sound carrier */
- int mixer;
- int left, right; /* volume */
+ int left, right; /* volume */
int bass, treble;
/* thread */
struct task_struct *thread;
- struct semaphore *wait;
+ struct wait_queue *wq;
struct semaphore *notify;
int active,restart,rmmod;
@@ -88,7 +97,22 @@ struct msp3400c {
#define dprintk if (debug) printk
+#if LINUX_VERSION_CODE < 0x020100
+/* 2.0.x */
+#define signal_pending(current) (current->signal & ~current->blocked)
+#define sigfillset(set)
+#define mdelay(x) udelay(1000*x)
+#else
MODULE_PARM(debug,"i");
+#endif
+
+#if LINUX_VERSION_CODE < 0x02017f
+void schedule_timeout(int j)
+{
+ current->timeout = jiffies + j;
+ schedule();
+}
+#endif
/* ---------------------------------------------------------------------- */
@@ -211,9 +235,9 @@ static struct MSP_INIT_DATA_DEM {
/* 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, 0x3002 },
+ 0x00d0, 0x0480, 0x0020, 0x3000 },
- /* Terrestial FM-mono */
+ /* 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},
@@ -230,7 +254,7 @@ static struct MSP_INIT_DATA_DEM {
/* NICAM I */
{ { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 },
- MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+ MSP_CARRIER(6.0), MSP_CARRIER(6.0),
0x00d0, 0x0040, 0x0120, 0x3000},
};
@@ -244,7 +268,7 @@ static struct CARRIER_DETECT carrier_detect_main[] = {
{ 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 SAT / SECAM" }
+ { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" }
};
static struct CARRIER_DETECT carrier_detect_55[] = {
@@ -255,6 +279,9 @@ static struct CARRIER_DETECT carrier_detect_55[] = {
static struct CARRIER_DETECT carrier_detect_65[] = {
/* PAL SAT / SECAM */
+ { MSP_CARRIER(5.85), "5.85 PAL D/K 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" },
@@ -270,6 +297,7 @@ static void msp3400c_setcarrier(struct i2c_bus *bus, int cdo1, int cdo2)
msp3400c_write(bus,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12);
msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff);
msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12);
+ msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
}
static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right)
@@ -329,12 +357,14 @@ static void msp3400c_setmode(struct msp3400c *msp, int type)
msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005,
msp_init_data[type].fir2[i]);
- msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */
+ msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */
msp_init_data[type].mode_reg);
msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1,
msp_init_data[type].cdo2);
+ msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
+
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,
msp_init_data[type].dfp_src);
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,
@@ -357,9 +387,9 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode)
/* switch demodulator */
switch (msp->mode) {
case MSP_MODE_FM_TERRA:
- dprintk("msp3400: B/G setstereo: %d\n",mode);
+ dprintk("msp3400: FM setstereo: %d\n",mode);
msp->stereo = mode;
- msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5));
+ msp3400c_setcarrier(msp->bus,msp->second,msp->main);
switch (mode) {
case VIDEO_SOUND_STEREO:
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001);
@@ -372,7 +402,7 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode)
}
break;
case MSP_MODE_FM_SAT:
- dprintk("msp3400: sat setstereo: %d\n",mode);
+ dprintk("msp3400: SAT setstereo: %d\n",mode);
msp->stereo = mode;
switch (mode) {
case VIDEO_SOUND_MONO:
@@ -390,9 +420,10 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode)
}
break;
case MSP_MODE_FM_NICAM1:
- dprintk("msp3400: NICAM1 setstereo: %d\n",mode);
+ case MSP_MODE_FM_NICAM2:
+ dprintk("msp3400: NICAM setstereo: %d\n",mode);
msp->stereo = mode;
- msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5));
+ msp3400c_setcarrier(msp->bus,msp->second,msp->main);
nicam=0x0100;
break;
default:
@@ -406,7 +437,9 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode)
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0020|nicam);
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0020|nicam);
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0020|nicam);
+#if 0
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000);
+#endif
break;
case VIDEO_SOUND_MONO:
case VIDEO_SOUND_LANG1:
@@ -422,6 +455,27 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode)
}
}
+static void
+msp3400c_print_mode(struct msp3400c *msp)
+{
+ if (msp->main == msp->second) {
+ printk("msp3400: mono sound carrier: %d.%03d MHz\n",
+ msp->main/910000,(msp->main/910)%1000);
+ } else {
+ printk("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("msp3400: NICAM carrier : %d.%03d MHz\n",
+ msp->second/910000,(msp->second/910)%1000);
+ if (msp->mode == MSP_MODE_FM_TERRA &&
+ msp->main != msp->second) {
+ printk("msp3400: FM-stereo carrier : %d.%03d MHz\n",
+ msp->second/910000,(msp->second/910)%1000);
+ }
+}
+
/* ----------------------------------------------------------------------- */
struct REGISTER_DUMP {
@@ -446,21 +500,21 @@ static void msp3400c_stereo_wake(unsigned long data)
{
struct msp3400c *msp = (struct msp3400c*)data; /* XXX alpha ??? */
- if (!msp->active)
- up(msp->wait);
+ wake_up_interruptible(&msp->wq);
}
static int msp3400c_thread(void *data)
{
- unsigned long flags;
struct msp3400c *msp = data;
- struct semaphore sem = MUTEX_LOCKED;
struct CARRIER_DETECT *cd;
int count, max1,max2,val1,val2, val,this;
int newstereo;
+ LOCK_FLAGS;
- /* lock_kernel(); */
+#ifdef __SMP__
+ lock_kernel();
+#endif
exit_mm(current);
current->session = 1;
@@ -469,10 +523,12 @@ static int msp3400c_thread(void *data)
current->fs->umask = 0;
strcpy(current->comm,"msp3400");
- msp->wait = &sem;
+ msp->wq = NULL;
msp->thread = current;
- /* unlock_kernel(); */
+#ifdef __SMP__
+ unlock_kernel();
+#endif
dprintk("msp3400: thread: start\n");
if(msp->notify != NULL)
@@ -481,9 +537,11 @@ static int msp3400c_thread(void *data)
for (;;) {
if (msp->rmmod)
goto done;
- dprintk("msp3400: thread: sleep\n");
- down_interruptible(&sem);
- dprintk("msp3400: thread: wakeup\n");
+ if (debug > 1)
+ printk("msp3400: thread: sleep\n");
+ interruptible_sleep_on(&msp->wq);
+ if (debug > 1)
+ printk("msp3400: thread: wakeup\n");
if (msp->rmmod || signal_pending(current))
goto done;
@@ -510,6 +568,7 @@ static int msp3400c_thread(void *data)
}
break;
case MSP_MODE_FM_NICAM1:
+ case MSP_MODE_FM_NICAM2:
val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23);
switch ((val & 0x1e) >> 1) {
case 0:
@@ -553,7 +612,7 @@ static int msp3400c_thread(void *data)
msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
UNLOCK_I2C_BUS(msp->bus);
- current->state = TASK_INTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/25);
if (signal_pending(current))
goto done;
@@ -587,7 +646,7 @@ static int msp3400c_thread(void *data)
msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
UNLOCK_I2C_BUS(msp->bus);
- current->state = TASK_INTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/25);
if (signal_pending(current))
goto done;
@@ -603,34 +662,56 @@ static int msp3400c_thread(void *data)
dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name);
}
- /* program the msp3400 according to the results */
+ /* programm the msp3400 according to the results */
+ msp->main = carrier_detect_main[max1].cdo;
switch (max1) {
- case 0: /* 4.5 */
case 1: /* 5.5 */
- msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
- msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
- carrier_detect_main[max1].cdo);
if (max2 == 0) {
/* B/G FM-stereo */
+ msp->second = carrier_detect_55[max2].cdo;
+ msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
msp->watch_stereo = 1;
- }
- if (max2 == 1 && msp->nicam) {
+ } else if (max2 == 1 && msp->nicam) {
/* B/G NICAM */
+ msp->second = carrier_detect_55[max2].cdo;
msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
- /* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */
- msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85),
- MSP_CARRIER(5.5));
+ msp3400c_setcarrier(msp->bus, 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(msp, MSP_MODE_FM_NICAM2);
+ msp3400c_setcarrier(msp->bus, 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(msp, MSP_MODE_FM_TERRA);
+ msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
+ msp->watch_stereo = 1;
+ } else if (max2 == 0 && msp->nicam) {
+ /* D/K NICAM */
+ msp->second = carrier_detect_65[max2].cdo;
+ msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
+ msp3400c_setcarrier(msp->bus, 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(msp, MSP_MODE_FM_TERRA);
- msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
- carrier_detect_main[max1].cdo);
- msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
+ msp3400c_setcarrier(msp->bus, msp->second, msp->main);
break;
}
@@ -640,15 +721,18 @@ static int msp3400c_thread(void *data)
if (msp->watch_stereo) {
del_timer(&msp->wake_stereo);
- msp->wake_stereo.expires = jiffies + HZ;
+ msp->wake_stereo.expires = jiffies + 2*HZ;
add_timer(&msp->wake_stereo);
}
+
+ if (debug)
+ msp3400c_print_mode(msp);
+
msp->active = 0;
}
done:
dprintk("msp3400: thread: exit\n");
- msp->wait = NULL;
msp->active = 0;
msp->thread = NULL;
@@ -690,6 +774,7 @@ static int msp3410d_thread(void *data)
goto done;
dprintk("msp3410: thread: sleep\n");
down_interruptible(&sem);
+ sem.owner = 0;
dprintk("msp3410: thread: wakeup\n");
if (msp->rmmod)
goto done;
@@ -713,7 +798,8 @@ static int msp3410d_thread(void *data)
/* wait 1 sec */
current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ);
+ current->timeout = jiffies + HZ;
+ schedule();
if (signal_pending(current))
goto done;
if (msp->restart) {
@@ -752,9 +838,14 @@ done:
#ifdef REGISTER_MIXER
+#include <linux/sound.h>
#include <linux/soundcard.h>
-#include <../drivers/sound/sound_config.h>
-#include <../drivers/sound/dev_table.h>
+#include <asm/uaccess.h>
+
+static struct msp3400c *mspmix = NULL; /* ugly hack, should do something more sensible */
+static int mixer_num;
+static int mixer_modcnt = 0;
+static struct semaphore mixer_sem = MUTEX;
static int mix_to_v4l(int i)
{
@@ -787,16 +878,42 @@ static int v4l_to_mix2(int l, int r)
return (r << 8) | l;
}
-static int msp3400c_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int
+msp3400c_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct msp3400c *msp = mixer_devs[dev]->devc;
- unsigned long flags;
int ret,val = 0;
+ LOCK_FLAGS;
+
+ if (cmd == SOUND_MIXER_INFO) {
+ mixer_info info;
+ strncpy(info.id, "MSP3400", sizeof(info.id));
+ strncpy(info.name, "MSP 3400", sizeof(info.name));
+ info.modify_counter = mixer_modcnt;
+ if (copy_to_user((void *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == SOUND_OLD_MIXER_INFO) {
+ _old_mixer_info info;
+ strncpy(info.id, "MSP3400", sizeof(info.id));
+ strncpy(info.name, "MSP 3400", sizeof(info.name));
+ if (copy_to_user((void *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == OSS_GETVERSION)
+ return put_user(SOUND_VERSION, (int *)arg);
if (_SIOC_DIR(cmd) & _SIOC_WRITE)
if (get_user(val, (int *)arg))
return -EFAULT;
+ down(&mixer_sem);
+ if (!mspmix) {
+ up(&mixer_sem);
+ return -ENODEV;
+ }
+
switch (cmd) {
case MIXER_READ(SOUND_MIXER_RECMASK):
case MIXER_READ(SOUND_MIXER_CAPS):
@@ -813,91 +930,97 @@ static int msp3400c_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
break;
case MIXER_WRITE(SOUND_MIXER_VOLUME):
- msp->left = mix_to_v4l(val);
- msp->right = mix_to_v4l(val >> 8);
- LOCK_I2C_BUS(msp->bus);
- msp3400c_setvolume(msp->bus,msp->left,msp->right);
- UNLOCK_I2C_BUS(msp->bus);
+ mspmix->left = mix_to_v4l(val);
+ mspmix->right = mix_to_v4l(val >> 8);
+ LOCK_I2C_BUS(mspmix->bus);
+ msp3400c_setvolume(mspmix->bus,mspmix->left,mspmix->right);
+ UNLOCK_I2C_BUS(mspmix->bus);
+ mixer_modcnt++;
/* fall */
case MIXER_READ(SOUND_MIXER_VOLUME):
- ret = v4l_to_mix2(msp->left, msp->right);
+ ret = v4l_to_mix2(mspmix->left, mspmix->right);
break;
case MIXER_WRITE(SOUND_MIXER_BASS):
- msp->bass = mix_to_v4l(val);
- LOCK_I2C_BUS(msp->bus);
- msp3400c_setbass(msp->bus,msp->bass);
- UNLOCK_I2C_BUS(msp->bus);
+ mspmix->bass = mix_to_v4l(val);
+ LOCK_I2C_BUS(mspmix->bus);
+ msp3400c_setbass(mspmix->bus,mspmix->bass);
+ UNLOCK_I2C_BUS(mspmix->bus);
+ mixer_modcnt++;
/* fall */
case MIXER_READ(SOUND_MIXER_BASS):
- ret = v4l_to_mix(msp->bass);
+ ret = v4l_to_mix(mspmix->bass);
break;
case MIXER_WRITE(SOUND_MIXER_TREBLE):
- msp->treble = mix_to_v4l(val);
- LOCK_I2C_BUS(msp->bus);
- msp3400c_settreble(msp->bus,msp->treble);
- UNLOCK_I2C_BUS(msp->bus);
+ mspmix->treble = mix_to_v4l(val);
+ LOCK_I2C_BUS(mspmix->bus);
+ msp3400c_settreble(mspmix->bus,mspmix->treble);
+ UNLOCK_I2C_BUS(mspmix->bus);
+ mixer_modcnt++;
/* fall */
case MIXER_READ(SOUND_MIXER_TREBLE):
- ret = v4l_to_mix(msp->treble);
+ ret = v4l_to_mix(mspmix->treble);
break;
default:
+ up(&mixer_sem);
return -EINVAL;
}
+ up(&mixer_sem);
if (put_user(ret, (int *)arg))
return -EFAULT;
return 0;
}
-struct mixer_operations msp3400c_mixer = {
- "video4linux",
- "TV card sound (msp3400)",
- msp3400c_mixer_ioctl
-};
-
-static int msp3400c_mixer_init(struct msp3400c *msp)
+static int
+msp3400c_mixer_open(struct inode *inode, struct file *file)
{
- int m;
-
- msp->mixer = m = sound_alloc_mixerdev();
- if (m == -1)
- return -1;
-
- mixer_devs[m] = (struct mixer_operations *)
- kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
- if (mixer_devs[m] == NULL) {
- printk(KERN_ERR "msp3400c: can't allocate memory\n");
- sound_unload_mixerdev(m);
- return -1;
- }
- memcpy(mixer_devs[m],&msp3400c_mixer,sizeof(struct mixer_operations));
- mixer_devs[m]->devc = msp;
- return 0;
+ MOD_INC_USE_COUNT;
+ return 0;
}
-static int msp3400c_mixer_close(struct msp3400c *msp)
+static int
+msp3400c_mixer_release(struct inode *inode, struct file *file)
{
- int m = msp->mixer;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
- if (m != -1 ) {
- sound_unload_mixerdev(m);
- kfree(mixer_devs[m]);
- }
- return 0;
+static loff_t
+msp3400c_mixer_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
}
+static /*const*/ struct file_operations msp3400c_mixer_fops = {
+ &msp3400c_mixer_llseek,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ &msp3400c_mixer_ioctl,
+ NULL, /* mmap */
+ &msp3400c_mixer_open,
+ NULL,
+ &msp3400c_mixer_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
#endif
/* ----------------------------------------------------------------------- */
static int msp3400c_attach(struct i2c_device *device)
{
- unsigned long flags;
struct semaphore sem = MUTEX_LOCKED;
struct msp3400c *msp;
int rev1,rev2;
+ LOCK_FLAGS;
device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL);
if (NULL == msp)
@@ -913,17 +1036,24 @@ static int msp3400c_attach(struct i2c_device *device)
if (-1 == msp3400c_reset(msp->bus)) {
UNLOCK_I2C_BUS(msp->bus);
kfree(msp);
- return -EIO;
+ dprintk("msp3400: no chip found\n");
+ return -1;
}
+ rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e);
+ rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f);
+ if (0 == rev1 && 0 == rev2) {
+ UNLOCK_I2C_BUS(msp->bus);
+ kfree(msp);
+ printk("msp3400: error while reading chip version\n");
+ return -1;
+ }
+
msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
msp3400c_setvolume(msp->bus, msp->left, msp->right);
msp3400c_setbass(msp->bus, msp->bass);
msp3400c_settreble(msp->bus, msp->treble);
- rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e);
- rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f);
-
#if 0
/* this will turn on a 1kHz beep - might be useful for debugging... */
msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040);
@@ -932,7 +1062,7 @@ static int msp3400c_attach(struct i2c_device *device)
sprintf(device->name,"MSP34%02d%c-%c%d",
(rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f);
- msp->nicam = (((rev2>>8)&0xff) == 10) ? 1 : 0;
+ msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0;
/* timer for stereo checking */
msp->wake_stereo.function = msp3400c_stereo_wake;
@@ -940,19 +1070,20 @@ static int msp3400c_attach(struct i2c_device *device)
/* startup control thread */
MOD_INC_USE_COUNT;
+ msp->wq = NULL;
msp->notify = &sem;
kernel_thread(msp3400c_thread, (void *)msp, 0);
down(&sem);
msp->notify = NULL;
- if (!msp->active)
- up(msp->wait);
+ wake_up_interruptible(&msp->wq);
printk(KERN_INFO "msp3400: init: chip=%s",device->name);
if (msp->nicam)
printk(", has NICAM support");
#ifdef REGISTER_MIXER
- if (0 == msp3400c_mixer_init(msp))
- printk(", registered as sound mixer");
+ down(&mixer_sem);
+ mspmix = msp;
+ up(&mixer_sem);
#endif
printk("\n");
return 0;
@@ -960,12 +1091,14 @@ static int msp3400c_attach(struct i2c_device *device)
static int msp3400c_detach(struct i2c_device *device)
{
- unsigned long flags;
struct semaphore sem = MUTEX_LOCKED;
struct msp3400c *msp = (struct msp3400c*)device->data;
+ LOCK_FLAGS;
#ifdef REGISTER_MIXER
- msp3400c_mixer_close(msp);
+ down(&mixer_sem);
+ mspmix = NULL;
+ up(&mixer_sem);
#endif
/* shutdown control thread */
@@ -974,8 +1107,7 @@ static int msp3400c_detach(struct i2c_device *device)
{
msp->notify = &sem;
msp->rmmod = 1;
- if (!msp->active)
- up(msp->wait);
+ wake_up_interruptible(&msp->wq);
down(&sem);
msp->notify = NULL;
}
@@ -992,9 +1124,10 @@ static int msp3400c_detach(struct i2c_device *device)
static int msp3400c_command(struct i2c_device *device,
unsigned int cmd, void *arg)
{
- unsigned long flags;
struct msp3400c *msp = (struct msp3400c*)device->data;
int *iarg = (int*)arg;
+ __u16 *sarg = arg;
+ LOCK_FLAGS;
switch (cmd) {
case MSP_SET_RADIO:
@@ -1004,74 +1137,79 @@ static int msp3400c_command(struct i2c_device *device,
LOCK_I2C_BUS(msp->bus);
msp3400c_setmode(msp,MSP_MODE_FM_RADIO);
msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
+ msp3400c_setvolume(msp->bus,msp->left, msp->right);
UNLOCK_I2C_BUS(msp->bus);
break;
case MSP_SET_TVNORM:
msp->norm = *iarg;
break;
+ case MSP_SWITCH_MUTE:
+ /* channels switching step one -- mute */
+ msp->watch_stereo=0;
+ del_timer(&msp->wake_stereo);
+ LOCK_I2C_BUS(msp->bus);
+ msp3400c_setvolume(msp->bus,0,0);
+ UNLOCK_I2C_BUS(msp->bus);
+ break;
case MSP_NEWCHANNEL:
+ /* channels switching step two -- trigger sound carrier scan */
msp->watch_stereo=0;
del_timer(&msp->wake_stereo);
- if (!msp->active)
- up(msp->wait);
- else
+ if (msp->active)
msp->restart = 1;
+ wake_up_interruptible(&msp->wq);
break;
case MSP_GET_VOLUME:
- *iarg = (msp->left > msp->right) ? msp->left : msp->right;
+ *sarg = (msp->left > msp->right) ? msp->left : msp->right;
break;
case MSP_SET_VOLUME:
- msp->left = msp->right = *iarg;
+ msp->left = msp->right = *sarg;
LOCK_I2C_BUS(msp->bus);
msp3400c_setvolume(msp->bus,msp->left, msp->right);
UNLOCK_I2C_BUS(msp->bus);
break;
case MSP_GET_BASS:
- *iarg = msp->bass;
+ *sarg = msp->bass;
break;
case MSP_SET_BASS:
- msp->bass = *iarg;
+ msp->bass = *sarg;
LOCK_I2C_BUS(msp->bus);
msp3400c_setbass(msp->bus,msp->bass);
UNLOCK_I2C_BUS(msp->bus);
break;
case MSP_GET_TREBLE:
- *iarg = msp->treble;
+ *sarg = msp->treble;
break;
case MSP_SET_TREBLE:
- msp->treble = *iarg;
+ msp->treble = *sarg;
LOCK_I2C_BUS(msp->bus);
msp3400c_settreble(msp->bus,msp->treble);
UNLOCK_I2C_BUS(msp->bus);
break;
case MSP_GET_STEREO:
- *iarg = msp->stereo;
+ *sarg = msp->stereo;
break;
case MSP_SET_STEREO:
- if (*iarg) {
+ if (*sarg) {
msp->watch_stereo=0;
del_timer(&msp->wake_stereo);
LOCK_I2C_BUS(msp->bus);
- msp3400c_setstereo(msp,*iarg);
+ msp3400c_setstereo(msp,*sarg);
UNLOCK_I2C_BUS(msp->bus);
}
break;
case MSP_GET_DC:
LOCK_I2C_BUS(msp->bus);
- *iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
- (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c);
+ *sarg = ((int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
+ (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c));
UNLOCK_I2C_BUS(msp->bus);
break;
-
- case MSP_GET_UNIT:
- *iarg = msp->mixer;
- break;
-
+
default:
return -EINVAL;
}
@@ -1090,8 +1228,6 @@ struct i2c_driver i2c_driver_msp = {
msp3400c_command
};
-EXPORT_NO_SYMBOLS;
-
#ifdef MODULE
int init_module(void)
#else
@@ -1099,6 +1235,10 @@ int init_module(void)
#endif
{
i2c_register_driver(&i2c_driver_msp);
+#ifdef REGISTER_MIXER
+ if ((mixer_num = register_sound_mixer(&msp3400c_mixer_fops, -1)) < 0)
+ printk(KERN_ERR "msp3400c: cannot allocate mixer device\n");
+#endif
return 0;
}
@@ -1106,6 +1246,10 @@ int init_module(void)
void cleanup_module(void)
{
i2c_unregister_driver(&i2c_driver_msp);
+#ifdef REGISTER_MIXER
+ if (mixer_num >= 0)
+ unregister_sound_mixer(mixer_num);
+#endif
}
#endif