Commit 87410dab authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

V4L/DVB (5997): cx25840: fix audio mute handling and reporting

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: default avatarHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 37297805
...@@ -194,19 +194,34 @@ void cx25840_audio_set_path(struct i2c_client *client) ...@@ -194,19 +194,34 @@ void cx25840_audio_set_path(struct i2c_client *client)
static int get_volume(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 /* Volume runs +18dB to -96dB in 1/2dB steps
* change to fit the msp3400 -114dB to +12dB range */ * change to fit the msp3400 -114dB to +12dB range */
/* check PATH1_VOLUME */ /* check PATH1_VOLUME */
int vol = 228 - cx25840_read(client, 0x8d4); vol = 228 - cx25840_read(client, 0x8d4);
vol = (vol / 2) + 23; vol = (vol / 2) + 23;
return vol << 9; return vol << 9;
} }
static void set_volume(struct i2c_client *client, int volume) static void set_volume(struct i2c_client *client, int volume)
{ {
/* First convert the volume to msp3400 values (0-127) */ struct cx25840_state *state = i2c_get_clientdata(client);
int vol = volume >> 9; 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 /* now scale it up to cx25840 values
* -114dB to -96dB maps to 0 * -114dB to -96dB maps to 0
* this should be 19, but in my testing that was 4dB too loud */ * 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) ...@@ -284,30 +299,26 @@ static void set_balance(struct i2c_client *client, int balance)
static int get_mute(struct i2c_client *client) static int get_mute(struct i2c_client *client)
{ {
/* check SRC1_MUTE_EN */ struct cx25840_state *state = i2c_get_clientdata(client);
return cx25840_read(client, 0x8d3) & 0x2 ? 1 : 0;
return state->unmute_volume >= 0;
} }
static void set_mute(struct i2c_client *client, int mute) static void set_mute(struct i2c_client *client, int mute)
{ {
struct cx25840_state *state = i2c_get_clientdata(client); struct cx25840_state *state = i2c_get_clientdata(client);
if (state->aud_input != CX25840_AUDIO_SERIAL) { if (mute && state->unmute_volume == -1) {
/* Must turn off microcontroller in order to mute sound. int vol = get_volume(client);
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any set_volume(client, 0);
* changes to the mute register. */ state->unmute_volume = vol;
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 { else if (!mute && state->unmute_volume != -1) {
/* SRC1_MUTE_EN */ int vol = state->unmute_volume;
cx25840_and_or(client, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
state->unmute_volume = -1;
set_volume(client, vol);
} }
} }
......
...@@ -915,6 +915,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, ...@@ -915,6 +915,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
state->audclk_freq = 48000; state->audclk_freq = 48000;
state->pvr150_workaround = 0; state->pvr150_workaround = 0;
state->audmode = V4L2_TUNER_MODE_LANG1; state->audmode = V4L2_TUNER_MODE_LANG1;
state->unmute_volume = -1;
state->vbi_line_offset = 8; state->vbi_line_offset = 8;
state->id = id; state->id = id;
state->rev = device_id; state->rev = device_id;
...@@ -1066,9 +1067,10 @@ static void log_audio_status(struct i2c_client *client) ...@@ -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, "Detected audio standard: %s\n", p);
v4l_info(client, "Audio muted: %s\n", 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", 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) { switch (audio_config >> 4) {
case 0x00: p = "undefined"; break; case 0x00: p = "undefined"; break;
......
...@@ -42,6 +42,7 @@ struct cx25840_state { ...@@ -42,6 +42,7 @@ struct cx25840_state {
enum cx25840_audio_input aud_input; enum cx25840_audio_input aud_input;
u32 audclk_freq; u32 audclk_freq;
int audmode; int audmode;
int unmute_volume; /* -1 if not muted */
int vbi_line_offset; int vbi_line_offset;
u32 id; u32 id;
u32 rev; u32 rev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment