vserver 1.9.5.x5
[linux-2.6.git] / drivers / media / video / cx88 / cx88-tvaudio.c
index f6def3d..874d297 100644 (file)
@@ -1,4 +1,6 @@
 /*
 /*
+    $Id: cx88-tvaudio.c,v 1.24 2004/10/25 11:51:00 kraxel Exp $
+
     cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
 
      (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
     cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
 
      (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
@@ -16,9 +18,9 @@
 
     Some comes from the dscaler sources, one of the dscaler driver guy works
     for Conexant ...
 
     Some comes from the dscaler sources, one of the dscaler driver guy works
     for Conexant ...
-    
+
     -----------------------------------------------------------------------
     -----------------------------------------------------------------------
-    
+
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
 
 #include "cx88.h"
 
 static unsigned int audio_debug = 1;
 
 #include "cx88.h"
 
 static unsigned int audio_debug = 1;
-MODULE_PARM(audio_debug,"i");
+module_param(audio_debug,int,0644);
 MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]");
 
 #define dprintk(fmt, arg...)   if (audio_debug) \
 MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]");
 
 #define dprintk(fmt, arg...)   if (audio_debug) \
-       printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+       printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
 
 /* ----------------------------------------------------------- */
 
 
 /* ----------------------------------------------------------- */
 
@@ -93,7 +97,7 @@ struct rlist {
        u32 val;
 };
 
        u32 val;
 };
 
-static void set_audio_registers(struct cx8800_dev *dev,
+static void set_audio_registers(struct cx88_core *core,
                                const struct rlist *l)
 {
        int i;
                                const struct rlist *l)
 {
        int i;
@@ -115,7 +119,7 @@ static void set_audio_registers(struct cx8800_dev *dev,
        }
 }
 
        }
 }
 
-static void set_audio_start(struct cx8800_dev *dev,
+static void set_audio_start(struct cx88_core *core,
                            u32 mode, u32 ctl)
 {
        // mute
                            u32 mode, u32 ctl)
 {
        // mute
@@ -133,7 +137,7 @@ static void set_audio_start(struct cx8800_dev *dev,
        cx_write(AUD_CTL,           ctl);
 }
 
        cx_write(AUD_CTL,           ctl);
 }
 
-static void set_audio_finish(struct cx8800_dev *dev)
+static void set_audio_finish(struct cx88_core *core)
 {
        u32 volume;
 
 {
        u32 volume;
 
@@ -150,7 +154,7 @@ static void set_audio_finish(struct cx8800_dev *dev)
 
 /* ----------------------------------------------------------- */
 
 
 /* ----------------------------------------------------------- */
 
-static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
+static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap)
 {
        static const struct rlist btsc[] = {
                /* from dscaler */
 {
        static const struct rlist btsc[] = {
                /* from dscaler */
@@ -247,19 +251,19 @@ static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
        // dscaler: don't know why to set EN_FMRADIO_EN_RDS
        if (sap) {
                dprintk("%s SAP (status: unknown)\n",__FUNCTION__);
        // dscaler: don't know why to set EN_FMRADIO_EN_RDS
        if (sap) {
                dprintk("%s SAP (status: unknown)\n",__FUNCTION__);
-               set_audio_start(dev, 0x0001,
+               set_audio_start(core, 0x0001,
                                EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP);
                                EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP);
-               set_audio_registers(dev, btsc_sap);
+               set_audio_registers(core, btsc_sap);
        } else {
                dprintk("%s (status: known-good)\n",__FUNCTION__);
        } else {
                dprintk("%s (status: known-good)\n",__FUNCTION__);
-               set_audio_start(dev, 0x0001,
+               set_audio_start(core, 0x0001,
                                EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO);
                                EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO);
-               set_audio_registers(dev, btsc);
+               set_audio_registers(core, btsc);
        }
        }
-       set_audio_finish(dev);
+       set_audio_finish(core);
 }
 
 }
 
-static void set_audio_standard_NICAM(struct cx8800_dev *dev)
+static void set_audio_standard_NICAM(struct cx88_core *core)
 {
        static const struct rlist nicam_common[] = {
                /* from dscaler */
 {
        static const struct rlist nicam_common[] = {
                /* from dscaler */
@@ -316,24 +320,25 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev)
                 { /* end of list */ },
        };
 
                 { /* end of list */ },
        };
 
-        set_audio_start(dev, 0x0010,
+        set_audio_start(core, 0x0010,
                        EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO);
                        EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO);
-        set_audio_registers(dev, nicam_common);
-       switch (dev->tvaudio) {
+        set_audio_registers(core, nicam_common);
+       switch (core->tvaudio) {
        case WW_NICAM_I:
                dprintk("%s PAL-I NICAM (status: unknown)\n",__FUNCTION__);
        case WW_NICAM_I:
                dprintk("%s PAL-I NICAM (status: unknown)\n",__FUNCTION__);
-               set_audio_registers(dev, nicam_pal_i);
+               set_audio_registers(core, nicam_pal_i);
+               break;
        case WW_NICAM_BGDKL:
        case WW_NICAM_BGDKL:
-               dprintk("%s PAL NICAM (status: unknown)\n",__FUNCTION__);
-               set_audio_registers(dev, nicam_default);
+               dprintk("%s PAL-BGDK NICAM (status: unknown)\n",__FUNCTION__);
+               set_audio_registers(core, nicam_default);
                break;
        };
                break;
        };
-        set_audio_finish(dev);
+        set_audio_finish(core);
 }
 
 }
 
-static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
+static void set_audio_standard_NICAM_L(struct cx88_core *core)
 {
 {
-       /* This is officially wierd.. register dumps indicate windows
+       /* This is officially weird.. register dumps indicate windows
         * uses audio mode 4.. A2. Let's operate and find out. */
 
        static const struct rlist nicam_l[] = {
         * uses audio mode 4.. A2. Let's operate and find out. */
 
        static const struct rlist nicam_l[] = {
@@ -449,13 +454,13 @@ static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
        };
 
        dprintk("%s (status: unknown)\n",__FUNCTION__);
        };
 
        dprintk("%s (status: unknown)\n",__FUNCTION__);
-        set_audio_start(dev, 0x0004,
+        set_audio_start(core, 0x0004,
                        0 /* FIXME */);
                        0 /* FIXME */);
-       set_audio_registers(dev, nicam_l);
-        set_audio_finish(dev);
+       set_audio_registers(core, nicam_l);
+        set_audio_finish(core);
 }
 
 }
 
-static void set_audio_standard_A2(struct cx8800_dev *dev)
+static void set_audio_standard_A2(struct cx88_core *core)
 {
        /* from dscaler cvs */
        static const struct rlist a2_common[] = {
 {
        /* from dscaler cvs */
        static const struct rlist a2_common[] = {
@@ -545,26 +550,33 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
                { /* end of list */ },
        };
 
                { /* end of list */ },
        };
 
-       set_audio_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
-       set_audio_registers(dev, a2_common);
-       switch (dev->tvaudio) {
+       set_audio_start(core, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
+       set_audio_registers(core, a2_common);
+       switch (core->tvaudio) {
+       case WW_NICAM_I:
+               /* gives at least mono according to the dscaler guys */
+               /* so use use that while nicam is broken ...         */
+               dprintk("%s PAL-I mono (status: unknown)\n",__FUNCTION__);
+               set_audio_registers(core, a2_table1);
+               cx_write(AUD_CTL, EN_A2_FORCE_MONO1);
+               break;
        case WW_A2_BG:
                dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__);
        case WW_A2_BG:
                dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__);
-               set_audio_registers(dev, a2_table1);
+               set_audio_registers(core, a2_table1);
                break;
        case WW_A2_DK:
                dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__);
                break;
        case WW_A2_DK:
                dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__);
-               set_audio_registers(dev, a2_table2);
+               set_audio_registers(core, a2_table2);
                break;
        case WW_A2_M:
                dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__);
                break;
        case WW_A2_M:
                dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__);
-               set_audio_registers(dev, a2_table3);
+               set_audio_registers(core, a2_table3);
                break;
        };
                break;
        };
-       set_audio_finish(dev);
+       set_audio_finish(core);
 }
 
 }
 
-static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
+static void set_audio_standard_EIAJ(struct cx88_core *core)
 {
        static const struct rlist eiaj[] = {
                /* TODO: eiaj register settings are not there yet ... */
 {
        static const struct rlist eiaj[] = {
                /* TODO: eiaj register settings are not there yet ... */
@@ -573,12 +585,12 @@ static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
        };
        dprintk("%s (status: unknown)\n",__FUNCTION__);
 
        };
        dprintk("%s (status: unknown)\n",__FUNCTION__);
 
-       set_audio_start(dev, 0x0002, EN_EIAJ_AUTO_STEREO);
-       set_audio_registers(dev, eiaj);
-       set_audio_finish(dev);
+       set_audio_start(core, 0x0002, EN_EIAJ_AUTO_STEREO);
+       set_audio_registers(core, eiaj);
+       set_audio_finish(core);
 }
 
 }
 
-static void set_audio_standard_FM(struct cx8800_dev *dev)
+static void set_audio_standard_FM(struct cx88_core *core)
 {
 #if 0 /* FIXME */
        switch (dev->audio_properties.FM_deemphasis)
 {
 #if 0 /* FIXME */
        switch (dev->audio_properties.FM_deemphasis)
@@ -596,7 +608,7 @@ static void set_audio_standard_FM(struct cx8800_dev *dev)
                        cx_write(AUD_DEEMPH1_B0, 0x1C29);
                        cx_write(AUD_DEEMPH1_A1, 0x3FC66);
                        cx_write(AUD_DEEMPH1_B1, 0x399A);
                        cx_write(AUD_DEEMPH1_B0, 0x1C29);
                        cx_write(AUD_DEEMPH1_A1, 0x3FC66);
                        cx_write(AUD_DEEMPH1_B1, 0x399A);
-                       
+
                        break;
 
                case WW_FM_DEEMPH_75:
                        break;
 
                case WW_FM_DEEMPH_75:
@@ -618,50 +630,51 @@ static void set_audio_standard_FM(struct cx8800_dev *dev)
 #endif
 
        dprintk("%s (status: unknown)\n",__FUNCTION__);
 #endif
 
        dprintk("%s (status: unknown)\n",__FUNCTION__);
-       set_audio_start(dev, 0x0020, EN_FMRADIO_AUTO_STEREO);
+       set_audio_start(core, 0x0020, EN_FMRADIO_AUTO_STEREO);
 
        // AB: 10/2/01: this register is not being reset appropriately on occasion.
        cx_write(AUD_POLYPH80SCALEFAC,3);
 
 
        // AB: 10/2/01: this register is not being reset appropriately on occasion.
        cx_write(AUD_POLYPH80SCALEFAC,3);
 
-       set_audio_finish(dev);
+       set_audio_finish(core);
 }
 
 /* ----------------------------------------------------------- */
 
 }
 
 /* ----------------------------------------------------------- */
 
-void cx88_set_tvaudio(struct cx8800_dev *dev)
+void cx88_set_tvaudio(struct cx88_core *core)
 {
 {
-       switch (dev->tvaudio) {
+       switch (core->tvaudio) {
        case WW_BTSC:
        case WW_BTSC:
-               set_audio_standard_BTSC(dev,0);
+               set_audio_standard_BTSC(core,0);
                break;
                break;
-       case WW_NICAM_I:
+       // case WW_NICAM_I:
        case WW_NICAM_BGDKL:
        case WW_NICAM_BGDKL:
-               set_audio_standard_NICAM(dev);
+               set_audio_standard_NICAM(core);
                break;
                break;
+       case WW_NICAM_I:
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
-               set_audio_standard_A2(dev);
+               set_audio_standard_A2(core);
                break;
        case WW_EIAJ:
                break;
        case WW_EIAJ:
-               set_audio_standard_EIAJ(dev);
+               set_audio_standard_EIAJ(core);
                break;
        case WW_FM:
                break;
        case WW_FM:
-               set_audio_standard_FM(dev);
+               set_audio_standard_FM(core);
                break;
        case WW_SYSTEM_L_AM:
                break;
        case WW_SYSTEM_L_AM:
-               set_audio_standard_NICAM_L(dev);
+               set_audio_standard_NICAM_L(core);
                break;
        case WW_NONE:
        default:
                break;
        case WW_NONE:
        default:
-               printk("%s: unknown tv audio mode [%d]\n",
-                      dev->name, dev->tvaudio);
+               printk("%s/0: unknown tv audio mode [%d]\n",
+                      core->name, core->tvaudio);
                break;
        }
        return;
 }
 
                break;
        }
        return;
 }
 
-void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
 {
        static char *m[] = {"stereo", "dual mono", "mono", "sap"};
        static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"};
 {
        static char *m[] = {"stereo", "dual mono", "mono", "sap"};
        static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"};
@@ -670,16 +683,28 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
        reg   = cx_read(AUD_STATUS);
        mode  = reg & 0x03;
        pilot = (reg >> 2) & 0x03;
        reg   = cx_read(AUD_STATUS);
        mode  = reg & 0x03;
        pilot = (reg >> 2) & 0x03;
-       dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
-               reg, m[mode], p[pilot],
-               aud_ctl_names[cx_read(AUD_CTL) & 63]);
+
+       if (core->astat != reg)
+               dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
+                       reg, m[mode], p[pilot],
+                       aud_ctl_names[cx_read(AUD_CTL) & 63]);
+       core->astat = reg;
 
        t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
                V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
        t->rxsubchans = V4L2_TUNER_SUB_MONO;
        t->audmode    = V4L2_TUNER_MODE_MONO;
 
 
        t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
                V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
        t->rxsubchans = V4L2_TUNER_SUB_MONO;
        t->audmode    = V4L2_TUNER_MODE_MONO;
 
-       switch (dev->tvaudio) {
+       switch (core->tvaudio) {
+       case WW_BTSC:
+               t->capability = V4L2_TUNER_CAP_STEREO |
+                       V4L2_TUNER_CAP_SAP;
+               t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+               if (1 == pilot) {
+                       /* SAP */
+                       t->rxsubchans |= V4L2_TUNER_SUB_SAP;
+               }
+               break;
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
@@ -707,17 +732,33 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
        return;
 }
 
        return;
 }
 
-void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
+void cx88_set_stereo(struct cx88_core *core, u32 mode)
 {
        u32 ctl  = UNSET;
        u32 mask = UNSET;
 
 {
        u32 ctl  = UNSET;
        u32 mask = UNSET;
 
-       switch (dev->tvaudio) {
+       switch (core->tvaudio) {
+       case WW_BTSC:
+               switch (mode) {
+               case V4L2_TUNER_MODE_MONO:
+                       ctl  = EN_BTSC_FORCE_MONO;
+                       mask = 0x3f;
+                       break;
+               case V4L2_TUNER_MODE_SAP:
+                       ctl  = EN_BTSC_FORCE_SAP;
+                       mask = 0x3f;
+                       break;
+               case V4L2_TUNER_MODE_STEREO:
+                       ctl  = EN_BTSC_AUTO_STEREO;
+                       mask = 0x3f;
+                       break;
+               }
+               break;
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
                switch (mode) {
        case WW_A2_BG:
        case WW_A2_DK:
        case WW_A2_M:
                switch (mode) {
-               case V4L2_TUNER_MODE_MONO:   
+               case V4L2_TUNER_MODE_MONO:
                case V4L2_TUNER_MODE_LANG1:
                        ctl  = EN_A2_FORCE_MONO1;
                        mask = 0x3f;
                case V4L2_TUNER_MODE_LANG1:
                        ctl  = EN_A2_FORCE_MONO1;
                        mask = 0x3f;
@@ -734,7 +775,7 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
                break;
        case WW_NICAM_BGDKL:
                switch (mode) {
                break;
        case WW_NICAM_BGDKL:
                switch (mode) {
-               case V4L2_TUNER_MODE_MONO:   
+               case V4L2_TUNER_MODE_MONO:
                        ctl  = EN_NICAM_FORCE_MONO1;
                        mask = 0x3f;
                        break;
                        ctl  = EN_NICAM_FORCE_MONO1;
                        mask = 0x3f;
                        break;
@@ -747,10 +788,10 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
                        mask = 0x93f;
                        break;
                }
                        mask = 0x93f;
                        break;
                }
-               break;  
+               break;
        case WW_FM:
                switch (mode) {
        case WW_FM:
                switch (mode) {
-               case V4L2_TUNER_MODE_MONO:   
+               case V4L2_TUNER_MODE_MONO:
                        ctl  = EN_FMRADIO_FORCE_MONO;
                        mask = 0x3f;
                        break;
                        ctl  = EN_FMRADIO_FORCE_MONO;
                        mask = 0x3f;
                        break;
@@ -759,7 +800,7 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
                        mask = 0x3f;
                        break;
                }
                        mask = 0x3f;
                        break;
                }
-               break;  
+               break;
        }
 
        if (UNSET != ctl) {
        }
 
        if (UNSET != ctl) {
@@ -774,32 +815,33 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
        return;
 }
 
        return;
 }
 
-/* just monitor the audio status for now ... */
 int cx88_audio_thread(void *data)
 {
 int cx88_audio_thread(void *data)
 {
-       struct cx8800_dev *dev = data;
+       struct cx88_core *core = data;
        struct v4l2_tuner t;
 
        struct v4l2_tuner t;
 
-       daemonize("msp3400");
-       allow_signal(SIGTERM);
        dprintk("cx88: tvaudio thread started\n");
        dprintk("cx88: tvaudio thread started\n");
-
        for (;;) {
        for (;;) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ*3);
-               if (signal_pending(current))
-                       break;
-               if (dev->shutdown)
+               if (kthread_should_stop())
                        break;
 
                        break;
 
+               /* just monitor the audio status for now ... */
                memset(&t,0,sizeof(t));
                memset(&t,0,sizeof(t));
-               cx88_get_stereo(dev,&t);
+               cx88_get_stereo(core,&t);
+               msleep_interruptible(1000);
        }
 
        dprintk("cx88: tvaudio thread exiting\n");
        }
 
        dprintk("cx88: tvaudio thread exiting\n");
-        complete_and_exit(&dev->texit, 0);
+       return 0;
 }
 
 }
 
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx88_set_tvaudio);
+EXPORT_SYMBOL(cx88_set_stereo);
+EXPORT_SYMBOL(cx88_get_stereo);
+EXPORT_SYMBOL(cx88_audio_thread);
+
 /*
  * Local variables:
  * c-basic-offset: 8
 /*
  * Local variables:
  * c-basic-offset: 8