V4L/DVB (5997): cx25840: fix audio mute handling and reporting
authorHans Verkuil <hverkuil@xs4all.nl>
Sun, 5 Aug 2007 11:00:36 +0000 (08:00 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 10 Oct 2007 01:04:55 +0000 (22:04 -0300)
Audio muting for the tuner input was implemented by stopping the
audio microcontroller and restarting it on unmute. However, it
appears that this method can actually crash the audio firmware.
It's rare and seems to happen with NTSC only.

It has been reimplemented by setting to volume to 0. In addition, the
reporting of the mute state has been improved as well: it used to be
impossible to detect whether the audio was muted by the user or if it
was muted due to the microcontroller trying to detect the audio
standard. This is now clearly stated.

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

index f897c1ebd5f3d809f4bf6bf6f111b2754395a4bc..f93b5160bb4fda46145e61498c8e586a23e0609b 100644 (file)
@@ -194,19 +194,34 @@ void cx25840_audio_set_path(struct i2c_client *client)
 
 static int get_volume(struct i2c_client *client)
 {
+       struct cx25840_state *state = i2c_get_clientdata(client);
+       int vol;
+
+       if (state->unmute_volume >= 0)
+               return state->unmute_volume;
+
        /* Volume runs +18dB to -96dB in 1/2dB steps
         * change to fit the msp3400 -114dB to +12dB range */
 
        /* check PATH1_VOLUME */
-       int vol = 228 - cx25840_read(client, 0x8d4);
+       vol = 228 - cx25840_read(client, 0x8d4);
        vol = (vol / 2) + 23;
        return vol << 9;
 }
 
 static void set_volume(struct i2c_client *client, int volume)
 {
-       /* First convert the volume to msp3400 values (0-127) */
-       int vol = volume >> 9;
+       struct cx25840_state *state = i2c_get_clientdata(client);
+       int vol;
+
+       if (state->unmute_volume >= 0) {
+               state->unmute_volume = volume;
+               return;
+       }
+
+       /* Convert the volume to msp3400 values (0-127) */
+       vol = volume >> 9;
+
        /* now scale it up to cx25840 values
         * -114dB to -96dB maps to 0
         * this should be 19, but in my testing that was 4dB too loud */
@@ -284,30 +299,26 @@ static void set_balance(struct i2c_client *client, int balance)
 
 static int get_mute(struct i2c_client *client)
 {
-       /* check SRC1_MUTE_EN */
-       return cx25840_read(client, 0x8d3) & 0x2 ? 1 : 0;
+       struct cx25840_state *state = i2c_get_clientdata(client);
+
+       return state->unmute_volume >= 0;
 }
 
 static void set_mute(struct i2c_client *client, int mute)
 {
        struct cx25840_state *state = i2c_get_clientdata(client);
 
-       if (state->aud_input != CX25840_AUDIO_SERIAL) {
-               /* Must turn off microcontroller in order to mute sound.
-                * Not sure if this is the best method, but it does work.
-                * If the microcontroller is running, then it will undo any
-                * changes to the mute register. */
-               if (mute) {
-                       /* disable microcontroller */
-                       cx25840_and_or(client, 0x803, ~0x10, 0x00);
-                       cx25840_write(client, 0x8d3, 0x1f);
-               } else {
-                       /* enable microcontroller */
-                       cx25840_and_or(client, 0x803, ~0x10, 0x10);
-               }
-       } else {
-               /* SRC1_MUTE_EN */
-               cx25840_and_or(client, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+       if (mute && state->unmute_volume == -1) {
+               int vol = get_volume(client);
+
+               set_volume(client, 0);
+               state->unmute_volume = vol;
+       }
+       else if (!mute && state->unmute_volume != -1) {
+               int vol = state->unmute_volume;
+
+               state->unmute_volume = -1;
+               set_volume(client, vol);
        }
 }
 
index 9f99007d389b1235da324bbf453ffec05df64ff6..65ad7943dd9a5326f57935732bb46d069fe0eb51 100644 (file)
@@ -915,6 +915,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
        state->audclk_freq = 48000;
        state->pvr150_workaround = 0;
        state->audmode = V4L2_TUNER_MODE_LANG1;
+       state->unmute_volume = -1;
        state->vbi_line_offset = 8;
        state->id = id;
        state->rev = device_id;
@@ -1066,9 +1067,10 @@ static void log_audio_status(struct i2c_client *client)
        }
        v4l_info(client, "Detected audio standard:   %s\n", p);
        v4l_info(client, "Audio muted:               %s\n",
-                   (mute_ctl & 0x2) ? "yes" : "no");
+                   (state->unmute_volume >= 0) ? "yes" : "no");
        v4l_info(client, "Audio microcontroller:     %s\n",
-                   (download_ctl & 0x10) ? "running" : "stopped");
+                   (download_ctl & 0x10) ?
+                               ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
 
        switch (audio_config >> 4) {
        case 0x00: p = "undefined"; break;
index 8c1fbd9b87c1bdb165c3a7b5d9cd1a2f10c1585b..86e2edfc494191651dee1f23ae4cf57ace726c55 100644 (file)
@@ -42,6 +42,7 @@ struct cx25840_state {
        enum cx25840_audio_input aud_input;
        u32 audclk_freq;
        int audmode;
+       int unmute_volume; /* -1 if not muted */
        int vbi_line_offset;
        u32 id;
        u32 rev;