V4L/DVB (3580): Last round of msp3400 cleanups before adding routing commands
authorHans Verkuil <hverkuil@xs4all.nl>
Sun, 19 Mar 2006 11:21:56 +0000 (08:21 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 24 Mar 2006 19:26:57 +0000 (16:26 -0300)
Lots of cleanups:
- remove duplicate actions
- add D/K3 Dual FM-Stereo and D/K NICAM FM (HDEV3) support
- put prescales in the proper place
- add missing D/K NICAM
- msp34xxg_reset now only resets instead of also starting the autodetect
  (moved that to msp34xxg_thread)
- fix support for SAP.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/msp3400-driver.c
drivers/media/video/msp3400-kthreads.c
drivers/media/video/msp3400.h

index 2d59d041f368df6dfcad4777d35ded0c69b904f1..9a47ba22c133894ed7e61549e3b4ca76020b83f0 100644 (file)
@@ -622,6 +622,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
 
                if (va->mode != 0 && state->radio == 0) {
                        state->audmode = msp_mode_v4l1_to_v4l2(va->mode);
+                       msp_set_audmode(client);
                }
                break;
        }
@@ -707,7 +708,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
                if (scart >= 0) {
                        state->rxsubchans = V4L2_TUNER_SUB_STEREO;
                        msp_set_scart(client, scart, 0);
-                       msp_write_dsp(client, 0x000d, 0x1900);
                }
                msp_set_audmode(client);
                msp_wake_thread(client);
index 972f2c80dbe297d95c81d5fb50a75699030aaaae..88b216cc49d774d73e8f0b175eee78ca50185cb6 100644 (file)
@@ -44,11 +44,13 @@ static struct {
        { 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)" },
+       { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), "6.5/5.74  D/K3 Dual FM-Stereo" },
        { 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)" },
+       { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85  D/K NICAM FM (HDEV3)" },
        { 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" },
@@ -206,12 +208,13 @@ void msp3400c_set_mode(struct i2c_client *client, int mode)
        msp3400c_set_carrier(client, data->cdo1, data->cdo2);
 
        msp_set_source(client, data->dsp_src);
-       msp_write_dsp(client, 0x000e, data->dsp_matrix);
+       /* set prescales */
 
-       if (state->has_nicam) {
-               /* nicam prescale */
-               msp_write_dsp(client, 0x0010, 0x5a00); /* was: 0x3000 */
-       }
+       /* volume prescale for SCART (AM mono input) */
+       msp_write_dsp(client, 0x000d, 0x1900);
+       msp_write_dsp(client, 0x000e, data->dsp_matrix);
+       if (state->has_nicam) /* nicam prescale */
+               msp_write_dsp(client, 0x0010, 0x5a00);
 }
 
 /* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP,
@@ -270,7 +273,6 @@ static void msp3400c_set_audmode(struct i2c_client *client)
        case MSP_MODE_FM_NICAM2:
        case MSP_MODE_AM_NICAM:
                v4l_dbg(1, msp_debug, client, "NICAM set_audmode: %s\n",modestr);
-               msp3400c_set_carrier(client, state->second, state->main);
                if (state->nicam_on)
                        src = 0x0100;  /* NICAM */
                break;
@@ -426,8 +428,8 @@ static void watch_stereo(struct i2c_client *client)
 {
        struct msp_state *state = i2c_get_clientdata(client);
 
-       if (msp3400c_detect_stereo(client)) {
-               msp3400c_set_audmode(client);
+       if (msp_detect_stereo(client)) {
+               msp_set_audmode(client);
        }
 
        if (msp_once)
@@ -463,7 +465,7 @@ int msp3400c_thread(void *data)
 
                /* mute */
                msp_set_mute(client);
-               msp3400c_set_mode(client, MSP_MODE_AM_DETECT /* +1 */ );
+               msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
                val1 = val2 = 0;
                max1 = max2 = -1;
                state->watch_stereo = 0;
@@ -571,8 +573,6 @@ int msp3400c_thread(void *data)
                                state->second = msp3400c_carrier_detect_65[max2].cdo;
                                msp3400c_set_mode(client, MSP_MODE_AM_NICAM);
                                msp3400c_set_carrier(client, state->second, state->main);
-                               /* volume prescale for SCART (AM mono input) */
-                               msp_write_dsp(client, 0x000d, 0x1900);
                                state->watch_stereo = 1;
                        } else if (max2 == 0 && state->has_nicam) {
                                /* D/K NICAM */
@@ -650,7 +650,8 @@ int msp3410d_thread(void *data)
                if (msp_sleep(state,200))
                        goto restart;
 
-               /* start autodetect */
+               /* start autodetect. Note: autodetect is not supported for
+                  NTSC-M and radio, hence we force the standard in those cases. */
                if (state->radio)
                        std = 0x40;
                else
@@ -694,23 +695,19 @@ int msp3410d_thread(void *data)
                        v4l_dbg(1, msp_debug, client, "autodetection failed,"
                                " switching to backup standard: %s (0x%04x)\n",
                                msp_stdlist[8].name ? msp_stdlist[8].name : "unknown",val);
-                       val = 0x0009;
+                       state->std = val = 0x0009;
                        msp_write_dem(client, 0x20, val);
                }
 
-               /* set various prescales */
-               msp_write_dsp(client, 0x0d, 0x1900); /* scart */
-               msp_write_dsp(client, 0x0e, 0x2403); /* FM */
-               msp_write_dsp(client, 0x10, 0x5a00); /* nicam */
-
                /* set stereo */
                switch (val) {
                case 0x0008: /* B/G NICAM */
                case 0x000a: /* I NICAM */
-                       if (val == 0x0008)
-                               state->mode = MSP_MODE_FM_NICAM1;
-                       else
+               case 0x000b: /* D/K NICAM */
+                       if (val == 0x000a)
                                state->mode = MSP_MODE_FM_NICAM2;
+                       else
+                               state->mode = MSP_MODE_FM_NICAM1;
                        /* just turn on stereo */
                        state->rxsubchans = V4L2_TUNER_SUB_STEREO;
                        state->nicam_on = 1;
@@ -738,6 +735,7 @@ int msp3410d_thread(void *data)
                        /* scart routing (this doesn't belong here I think) */
                        msp_set_scart(client,SCART_IN2,0);
                        break;
+               case 0x0002:
                case 0x0003:
                case 0x0004:
                case 0x0005:
@@ -747,12 +745,19 @@ int msp3410d_thread(void *data)
                        break;
                }
 
-               /* unmute, restore misc registers */
-               msp_set_audio(client);
-               msp_write_dsp(client, 0x13, state->acb);
+               /* set various prescales */
+               msp_write_dsp(client, 0x0d, 0x1900); /* scart */
+               msp_write_dsp(client, 0x0e, 0x3000); /* FM */
+               if (state->has_nicam)
+                       msp_write_dsp(client, 0x10, 0x5a00); /* nicam */
+
                if (state->has_i2s_conf)
                        msp_write_dem(client, 0x40, state->i2s_mode);
 
+               /* unmute, restore misc registers */
+               msp_set_audio(client);
+
+               msp_write_dsp(client, 0x13, state->acb);
                msp3400c_set_audmode(client);
 
                /* monitor tv audio mode, the first time don't wait
@@ -771,16 +776,15 @@ int msp3410d_thread(void *data)
 
 /* ----------------------------------------------------------------------- */
 
-/* msp34xxG + (autoselect 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      */
+/* msp34xxG + (autoselect 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
+ */
 
 /* set the same 'source' for the loudspeaker, scart and quasi-peak detector
  * the value for source is the same as bit 15:8 of DSP 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_set_audmode
  */
 static void msp34xxg_set_source(struct i2c_client *client, int source)
 {
@@ -838,58 +842,56 @@ static int msp34xxg_modus(struct i2c_client *client)
        return 0x0001;
 }
 
-/* (re-)initialize the msp34xxg, according to the current norm in state->norm
- * return 0 if it worked, -1 if it failed
- */
-static int msp34xxg_reset(struct i2c_client *client)
+/* (re-)initialize the msp34xxg */
+static void msp34xxg_reset(struct i2c_client *client)
 {
        struct msp_state *state = i2c_get_clientdata(client);
-       int modus, std;
+       int modus;
 
-       if (msp_reset(client))
-               return -1;
+       /* initialize std to 1 (autodetect) to signal that no standard is
+          selected yet. */
+       state->std = 1;
+
+       msp_reset(client);
 
        /* make sure that input/output is muted (paranoid mode) */
        /* ACB, mute DSP input, mute SCART 1 */
-       if (msp_write_dsp(client, 0x13, 0x0f20))
-               return -1;
+       msp_write_dsp(client, 0x13, 0x0f20);
 
        if (state->has_i2s_conf)
                msp_write_dem(client, 0x40, state->i2s_mode);
 
        /* step-by-step initialisation, as described in the manual */
        modus = msp34xxg_modus(client);
-       if (state->radio)
-               std = 0x40;
-       else
-               std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1;
-       modus &= ~0x03; /* STATUS_CHANGE = 0 */
-       modus |= 0x01;  /* AUTOMATIC_SOUND_DETECTION = 1 */
-       if (msp_write_dem(client, 0x30, modus))
-               return -1;
-       if (msp_write_dem(client, 0x20, std))
-               return -1;
+       msp_write_dem(client, 0x30, modus);
 
        /* write the dsps that may have an influence on
           standard/audio autodetection right now */
        msp34xxg_set_source(client, state->source);
 
-       /* AM/FM Prescale [15:8] 75khz deviation */
-       if (msp_write_dsp(client, 0x0e, 0x3000))
-               return -1;
-
-       /* NICAM Prescale 9db gain (as recommended) */
-       if (msp_write_dsp(client, 0x10, 0x5a00))
-               return -1;
+       msp_write_dsp(client, 0x0d, 0x1900); /* scart */
+       msp_write_dsp(client, 0x0e, 0x3000); /* FM */
+       if (state->has_nicam)
+               msp_write_dsp(client, 0x10, 0x5a00); /* nicam */
 
-       return 0;
+       /* set identification threshold. Personally, I
+        * I set it to a higher value than the default
+        * of 0x190 to ignore noisy stereo signals.
+        * this needs tuning. (recommended range 0x00a0-0x03c0)
+        * 0x7f0 = forced mono mode
+        *
+        * a2 threshold for stereo/bilingual.
+        * Note: this register is part of the Manual/Compatibility mode.
+        * It is supported by all 'G'-family chips.
+        */
+       msp_write_dem(client, 0x22, msp_stereo_thresh);
 }
 
 int msp34xxg_thread(void *data)
 {
        struct i2c_client *client = data;
        struct msp_state *state = i2c_get_clientdata(client);
-       int val, std, i;
+       int val, i;
 
        v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
 
@@ -907,12 +909,14 @@ int msp34xxg_thread(void *data)
 
                /* setup the chip*/
                msp34xxg_reset(client);
-               std = msp_standard;
-               if (std != 0x01)
+               state->std = state->radio ? 0x40 : msp_standard;
+               if (state->std != 1)
                        goto unmute;
+               /* start autodetect */
+               msp_write_dem(client, 0x20, state->std);
 
                /* watch autodetect */
-               v4l_dbg(1, msp_debug, client, "triggered autodetect, waiting for result\n");
+               v4l_dbg(1, msp_debug, client, "started autodetect, waiting for result\n");
                for (i = 0; i < 10; i++) {
                        if (msp_sleep(state, 100))
                                goto restart;
@@ -920,20 +924,19 @@ int msp34xxg_thread(void *data)
                        /* check results */
                        val = msp_read_dem(client, 0x7e);
                        if (val < 0x07ff) {
-                               std = val;
+                               state->std = val;
                                break;
                        }
                        v4l_dbg(2, msp_debug, client, "detection still in progress\n");
                }
-               if (std == 1) {
+               if (state->std == 1) {
                        v4l_dbg(1, msp_debug, client, "detection still in progress after 10 tries. giving up.\n");
                        continue;
                }
 
        unmute:
-               state->std = std;
-               v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n",
-                       msp_standard_std_name(std), std);
+               v4l_dbg(1, msp_debug, client, "detected standard: %s (0x%04x)\n",
+                       msp_standard_std_name(state->std), state->std);
 
                /* unmute: dispatch sound to scart output, set scart volume */
                msp_set_audio(client);
@@ -942,20 +945,33 @@ int msp34xxg_thread(void *data)
                if (msp_write_dsp(client, 0x13, state->acb))
                        return -1;
 
-               if (state->has_i2s_conf)
-                       msp_write_dem(client, 0x40, state->i2s_mode);
+               /* the periodic stereo/SAP check is only relevant for
+                  the 0x20 standard (BTSC) */
+               if (state->std != 0x20)
+                       continue;
+
+               state->watch_stereo = 1;
+
+               /* monitor tv audio mode, the first time don't wait
+                  in order to get a quick stereo/SAP update */
+               watch_stereo(client);
+               while (state->watch_stereo) {
+                       watch_stereo(client);
+                       if (msp_sleep(state, 5000))
+                               goto restart;
+               }
        }
        v4l_dbg(1, msp_debug, client, "thread: exit\n");
        return 0;
 }
 
-static void msp34xxg_detect_stereo(struct i2c_client *client)
+static int msp34xxg_detect_stereo(struct i2c_client *client)
 {
        struct msp_state *state = i2c_get_clientdata(client);
-
        int status = msp_read_dem(client, 0x0200);
        int is_bilingual = status & 0x100;
        int is_stereo = status & 0x40;
+       int oldrx = state->rxsubchans;
 
        state->rxsubchans = 0;
        if (is_stereo)
@@ -963,16 +979,14 @@ static void msp34xxg_detect_stereo(struct i2c_client *client)
        else
                state->rxsubchans = V4L2_TUNER_SUB_MONO;
        if (is_bilingual) {
-               state->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.
-                */
+               if (state->std == 0x20)
+                       state->rxsubchans |= V4L2_TUNER_SUB_SAP;
+               else
+                       state->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
        }
        v4l_dbg(1, msp_debug, client, "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",
                status, is_stereo, is_bilingual, state->rxsubchans);
+       return (oldrx != state->rxsubchans);
 }
 
 static void msp34xxg_set_audmode(struct i2c_client *client)
@@ -980,6 +994,16 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
        struct msp_state *state = i2c_get_clientdata(client);
        int source;
 
+       if (state->std == 0x20) {
+              if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
+                  (state->audmode == V4L2_TUNER_MODE_STEREO ||
+                   state->audmode == V4L2_TUNER_MODE_LANG2)) {
+                       msp_write_dem(client, 0x20, 0x21);
+              } else {
+                       msp_write_dem(client, 0x20, 0x20);
+              }
+       }
+
        switch (state->audmode) {
        case V4L2_TUNER_MODE_MONO:
                source = 0; /* mono only */
@@ -1008,7 +1032,6 @@ void msp_set_audmode(struct i2c_client *client)
        switch (state->opmode) {
        case OPMODE_MANUAL:
        case OPMODE_AUTODETECT:
-               state->watch_stereo = 0;
                msp3400c_set_audmode(client);
                break;
        case OPMODE_AUTOSELECT:
@@ -1017,18 +1040,17 @@ void msp_set_audmode(struct i2c_client *client)
        }
 }
 
-void msp_detect_stereo(struct i2c_client *client)
+int msp_detect_stereo(struct i2c_client *client)
 {
        struct msp_state *state  = i2c_get_clientdata(client);
 
        switch (state->opmode) {
        case OPMODE_MANUAL:
        case OPMODE_AUTODETECT:
-               msp3400c_detect_stereo(client);
-               break;
+               return msp3400c_detect_stereo(client);
        case OPMODE_AUTOSELECT:
-               msp34xxg_detect_stereo(client);
-               break;
+               return msp34xxg_detect_stereo(client);
        }
+       return 0;
 }
 
index c4553f50aa408a484a8e738f3cac7f9fd36fc4de..25482046fc68f4ea509ea0d50598803ed9df4e8f 100644 (file)
@@ -103,7 +103,7 @@ int msp_sleep(struct msp_state *state, int timeout);
 /* msp3400-kthreads.c */
 const char *msp_standard_std_name(int std);
 void msp_set_audmode(struct i2c_client *client);
-void msp_detect_stereo(struct i2c_client *client);
+int msp_detect_stereo(struct i2c_client *client);
 int msp3400c_thread(void *data);
 int msp3410d_thread(void *data);
 int msp34xxg_thread(void *data);