- Check capabilities for audio settings (volume, balance,
bass, treble, loudness, mute)
- added loudness support
- added missing VIDEO_AUDIO_BALANCE flags for v4l1 compatibility
- do not call msp_any_detect_stereo for non-autoselect chips to
retrieve the current stereo setting: that will temporarily mute
the sound. It is only needed when the stereo mode might be
changed, and for autoselect msp processors that do not periodically
need to update their stereo setting.
- do not wake up the thread if the standard did not change. Prevents
temporary audio drop-out if the standard is set to the same value.
- fix confused stereo detect code where V4L2_TUNER_SUB_STEREO and
V4L2_TUNER_MODE_STEREO values were used incorrectly.
- stereo mode reporting was broken (v4l2 value used to index a
string array expecting v4l1 mode values).
- do not set dsp register 0x30 in the 3410d thread: that register
does not exist for pre-'G' revision msp chips.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
void msp_set_mute(struct i2c_client *client)
{
void msp_set_mute(struct i2c_client *client)
{
+ struct msp_state *state = i2c_get_clientdata(client);
+
v4l_dbg(1, client, "mute audio\n");
v4l_dbg(1, client, "mute audio\n");
- msp_write_dsp(client, 0x0000, 0); /* loudspeaker */
- msp_write_dsp(client, 0x0006, 0); /* headphones */
+ msp_write_dsp(client, 0x0000, 0);
+ msp_write_dsp(client, 0x0007, 1);
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, 1);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, 0);
}
void msp_set_audio(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
}
void msp_set_audio(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
- int val = 0, bal = 0, bass, treble;
+ int bal = 0, bass, treble, loudness;
+ int val = 0;
if (!state->muted)
val = (state->volume * 0x7f / 65535) << 8;
if (!state->muted)
val = (state->volume * 0x7f / 65535) << 8;
+
+ v4l_dbg(1, client, "mute=%s volume=%d\n",
+ state->muted ? "on" : "off", state->volume);
+
+ msp_write_dsp(client, 0x0000, val);
+ msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1));
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1));
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, val);
+ if (!state->has_sound_processing)
+ return;
+
- bal = (state->balance / 256) - 128;
+ bal = (u8)((state->balance / 256) - 128);
bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
+ loudness = state->loudness ? ((5 * 4) << 8) : 0;
- v4l_dbg(1, client, "mute=%s volume=%d balance=%d bass=%d treble=%d\n",
- state->muted ? "on" : "off", state->volume, state->balance,
- state->bass, state->treble);
+ v4l_dbg(1, client, "balance=%d bass=%d treble=%d loudness=%d\n",
+ state->balance, state->bass, state->treble, state->loudness);
- msp_write_dsp(client, 0x0000, val); /* loudspeaker */
- msp_write_dsp(client, 0x0006, val); /* headphones */
- msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1));
msp_write_dsp(client, 0x0001, bal << 8);
msp_write_dsp(client, 0x0001, bal << 8);
- msp_write_dsp(client, 0x0002, bass); /* loudspeaker */
- msp_write_dsp(client, 0x0003, treble); /* loudspeaker */
+ msp_write_dsp(client, 0x0002, bass);
+ msp_write_dsp(client, 0x0003, treble);
+ msp_write_dsp(client, 0x0004, loudness);
+ if (!state->has_headphones)
+ return;
+ msp_write_dsp(client, 0x0030, bal << 8);
+ msp_write_dsp(client, 0x0031, bass);
+ msp_write_dsp(client, 0x0032, treble);
+ msp_write_dsp(client, 0x0033, loudness);
}
int msp_modus(struct i2c_client *client)
}
int msp_modus(struct i2c_client *client)
-static struct v4l2_queryctrl msp_qctrl[] = {
+static struct v4l2_queryctrl msp_qctrl_std[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
.default_value = 58880,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
.default_value = 58880,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
},{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.default_value = 1,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.default_value = 1,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+
+static struct v4l2_queryctrl msp_qctrl_sound_processing[] = {
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .name = "Balance",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_AUDIO_BASS,
.name = "Bass",
},{
.id = V4L2_CID_AUDIO_BASS,
.name = "Bass",
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
.step = 65535/100,
.default_value = 32768,
.type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .name = "Loudness",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
struct msp_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
struct msp_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = state->volume;
+ break;
+
case V4L2_CID_AUDIO_MUTE:
ctrl->value = state->muted;
break;
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
ctrl->value = state->muted;
break;
case V4L2_CID_AUDIO_BALANCE:
+ if (!state->has_sound_processing)
+ return -EINVAL;
ctrl->value = state->balance;
break;
case V4L2_CID_AUDIO_BASS:
ctrl->value = state->balance;
break;
case V4L2_CID_AUDIO_BASS:
+ if (!state->has_sound_processing)
+ return -EINVAL;
ctrl->value = state->bass;
break;
case V4L2_CID_AUDIO_TREBLE:
ctrl->value = state->bass;
break;
case V4L2_CID_AUDIO_TREBLE:
+ if (!state->has_sound_processing)
+ return -EINVAL;
ctrl->value = state->treble;
break;
ctrl->value = state->treble;
break;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->volume;
+ case V4L2_CID_AUDIO_LOUDNESS:
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ ctrl->value = state->loudness;
struct msp_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
struct msp_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ state->volume = ctrl->value;
+ if (state->volume == 0)
+ state->balance = 32768;
+ break;
+
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value < 0 || ctrl->value >= 2)
return -ERANGE;
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value < 0 || ctrl->value >= 2)
return -ERANGE;
break;
case V4L2_CID_AUDIO_BASS:
break;
case V4L2_CID_AUDIO_BASS:
+ if (!state->has_sound_processing)
+ return -EINVAL;
state->bass = ctrl->value;
break;
case V4L2_CID_AUDIO_TREBLE:
state->bass = ctrl->value;
break;
case V4L2_CID_AUDIO_TREBLE:
+ if (!state->has_sound_processing)
+ return -EINVAL;
state->treble = ctrl->value;
break;
state->treble = ctrl->value;
break;
- case V4L2_CID_AUDIO_BALANCE:
- state->balance = ctrl->value;
+ case V4L2_CID_AUDIO_LOUDNESS:
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ state->loudness = ctrl->value;
- case V4L2_CID_AUDIO_VOLUME:
- state->volume = ctrl->value;
- if (state->volume == 0)
- state->balance = 32768;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ state->balance = ctrl->value;
{
struct video_audio *va = arg;
{
struct video_audio *va = arg;
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE |
- VIDEO_AUDIO_MUTABLE;
- if (state->muted)
- va->flags |= VIDEO_AUDIO_MUTE;
-
+ va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE;
+ if (state->has_sound_processing)
+ va->flags |= VIDEO_AUDIO_BALANCE |
+ VIDEO_AUDIO_BASS |
+ VIDEO_AUDIO_TREBLE;
if (state->muted)
va->flags |= VIDEO_AUDIO_MUTE;
va->volume = state->volume;
if (state->muted)
va->flags |= VIDEO_AUDIO_MUTE;
va->volume = state->volume;
va->bass = state->bass;
va->treble = state->treble;
va->bass = state->bass;
va->treble = state->treble;
- msp_any_detect_stereo(client);
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_any_detect_stereo(client);
va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans);
break;
}
va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans);
break;
}
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
+ int update = 0;
+ v4l2_std_id std;
+ if (state->radio)
+ update = 1;
state->radio = 0;
if (vc->norm == VIDEO_MODE_PAL)
state->radio = 0;
if (vc->norm == VIDEO_MODE_PAL)
- state->std = V4L2_STD_PAL;
else if (vc->norm == VIDEO_MODE_SECAM)
else if (vc->norm == VIDEO_MODE_SECAM)
- state->std = V4L2_STD_SECAM;
- state->std = V4L2_STD_NTSC;
- msp_wake_thread(client);
+ std = V4L2_STD_NTSC;
+ if (std != state->std) {
+ state->std = std;
+ update = 1;
+ }
+ if (update)
+ msp_wake_thread(client);
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
+ int update = state->radio || state->std != *id;
state->std = *id;
state->radio = 0;
state->std = *id;
state->radio = 0;
- msp_wake_thread(client);
+ if (update)
+ msp_wake_thread(client);
{
struct v4l2_tuner *vt = arg;
{
struct v4l2_tuner *vt = arg;
- msp_any_detect_stereo(client);
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_any_detect_stereo(client);
vt->audmode = state->audmode;
vt->rxsubchans = state->rxsubchans;
vt->capability = V4L2_TUNER_CAP_STEREO |
vt->audmode = state->audmode;
vt->rxsubchans = state->rxsubchans;
vt->capability = V4L2_TUNER_CAP_STEREO |
struct v4l2_queryctrl *qc = arg;
int i;
struct v4l2_queryctrl *qc = arg;
int i;
- for (i = 0; i < ARRAY_SIZE(msp_qctrl); i++)
- if (qc->id && qc->id == msp_qctrl[i].id) {
- memcpy(qc, &msp_qctrl[i], sizeof(*qc));
+ for (i = 0; i < ARRAY_SIZE(msp_qctrl_std); i++)
+ if (qc->id && qc->id == msp_qctrl_std[i].id) {
+ memcpy(qc, &msp_qctrl_std[i], sizeof(*qc));
+ return 0;
+ }
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(msp_qctrl_sound_processing); i++)
+ if (qc->id && qc->id == msp_qctrl_sound_processing[i].id) {
+ memcpy(qc, &msp_qctrl_sound_processing[i], sizeof(*qc));
return 0;
}
return -EINVAL;
return 0;
}
return -EINVAL;
return msp_set_ctrl(client, arg);
case VIDIOC_LOG_STATUS:
return msp_set_ctrl(client, arg);
case VIDIOC_LOG_STATUS:
- msp_any_detect_stereo(client);
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_any_detect_stereo(client);
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
client->name, state->rev1, state->rev2);
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d balance %d bass %d treble %d%s\n",
- state->volume, state->balance,
- state->bass, state->treble,
- state->muted ? " (muted)" : "");
+ v4l_info(client, "Audio: volume %d%s\n",
+ state->volume, state->muted ? " (muted)" : "");
+ if (state->has_sound_processing) {
+ v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
+ state->balance, state->bass, state->treble,
+ state->loudness ? "on" : "off");
+ }
v4l_info(client, "Mode: %s (%s%s)\n", msp_standard_mode_name(state->mode),
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
v4l_info(client, "Mode: %s (%s%s)\n", msp_standard_mode_name(state->mode),
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
state->balance = 32768; /* 0db gain */
state->bass = 32768;
state->treble = 32768;
state->balance = 32768; /* 0db gain */
state->bass = 32768;
state->treble = 32768;
state->input = -1;
state->muted = 0;
state->i2s_mode = 0;
state->input = -1;
state->muted = 0;
state->i2s_mode = 0;
/* Has scart2 and scart3 inputs and scart2 output: not in stripped
down products of the '3' family */
state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
/* Has scart2 and scart3 inputs and scart2 output: not in stripped
down products of the '3' family */
state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
+ /* Has scart2 a volume control? Not in pre-D revisions. */
+ state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out;
/* Has subwoofer output: not in pre-D revs and not in stripped down products */
state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;
/* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in
/* Has subwoofer output: not in pre-D revs and not in stripped down products */
state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;
/* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in
-/* given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask */
-static int msp3400c_best_video_sound(int rxsubchans)
-{
- if (rxsubchans & V4L2_TUNER_SUB_STEREO)
- return V4L2_TUNER_MODE_STEREO;
- if (rxsubchans & V4L2_TUNER_SUB_LANG1)
- return V4L2_TUNER_MODE_LANG1;
- if (rxsubchans & V4L2_TUNER_SUB_LANG2)
- return V4L2_TUNER_MODE_LANG2;
- return V4L2_TUNER_MODE_MONO;
-}
-
/* turn on/off nicam + stereo */
void msp3400c_setstereo(struct i2c_client *client, int mode)
{
/* turn on/off nicam + stereo */
void msp3400c_setstereo(struct i2c_client *client, int mode)
{
- static char *strmode[] = { "0", "mono", "stereo", "3",
- "lang1", "5", "6", "7", "lang2"
- };
+ static char *strmode[] = { "mono", "stereo", "lang2", "lang1" };
struct msp_state *state = i2c_get_clientdata(client);
int nicam = 0; /* channel source: FM/AM or nicam */
int src = 0;
struct msp_state *state = i2c_get_clientdata(client);
int nicam = 0; /* channel source: FM/AM or nicam */
int src = 0;
switch (state->mode) {
case MSP_MODE_FM_TERRA:
v4l_dbg(1, client, "FM setstereo: %s\n", strmode[mode]);
switch (state->mode) {
case MSP_MODE_FM_TERRA:
v4l_dbg(1, client, "FM setstereo: %s\n", strmode[mode]);
- msp3400c_setcarrier(client,state->second,state->main);
+ msp3400c_setcarrier(client, state->second, state->main);
switch (mode) {
case V4L2_TUNER_MODE_STEREO:
msp_write_dsp(client, 0x000e, 0x3001);
switch (mode) {
case V4L2_TUNER_MODE_STEREO:
msp_write_dsp(client, 0x000e, 0x3001);
- switch (msp3400c_best_video_sound(mode)) {
case V4L2_TUNER_MODE_STEREO:
src = 0x0020 | nicam;
break;
case V4L2_TUNER_MODE_STEREO:
src = 0x0020 | nicam;
break;
msp_write_dsp(client, 0x0009, src);
msp_write_dsp(client, 0x000a, src);
msp_write_dsp(client, 0x000b, src);
msp_write_dsp(client, 0x0009, src);
msp_write_dsp(client, 0x000a, src);
msp_write_dsp(client, 0x000b, src);
+ msp_write_dsp(client, 0x000c, src);
+ if (state->has_scart23_in_scart2_out)
+ msp_write_dsp(client, 0x0041, src);
struct msp_state *state = i2c_get_clientdata(client);
if (autodetect_stereo(client)) {
struct msp_state *state = i2c_get_clientdata(client);
if (autodetect_stereo(client)) {
- if (state->stereo & V4L2_TUNER_MODE_STEREO)
+ if (state->rxsubchans & V4L2_TUNER_SUB_STEREO)
msp3400c_setstereo(client, V4L2_TUNER_MODE_STEREO);
msp3400c_setstereo(client, V4L2_TUNER_MODE_STEREO);
- else if (state->stereo & VIDEO_SOUND_LANG1)
+ else if (state->rxsubchans & V4L2_TUNER_SUB_LANG1)
msp3400c_setstereo(client, V4L2_TUNER_MODE_LANG1);
else
msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);
msp3400c_setstereo(client, V4L2_TUNER_MODE_LANG1);
else
msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);
{
struct i2c_client *client = data;
struct msp_state *state = i2c_get_clientdata(client);
{
struct i2c_client *client = data;
struct msp_state *state = i2c_get_clientdata(client);
v4l_dbg(1, client, "msp3410 daemon started\n");
v4l_dbg(1, client, "msp3410 daemon started\n");
goto restart;
/* start autodetect */
goto restart;
/* start autodetect */
- mode = msp_modus(client);
- std = (state->std & V4L2_STD_NTSC) ? 0x20 : 1;
- msp_write_dem(client, 0x30, mode);
- msp_write_dem(client, 0x20, std);
+ std = 1;
+ if (state->std & V4L2_STD_NTSC)
+ std = 0x20;
+ else
+ msp_write_dem(client, 0x20, std);
state->watch_stereo = 0;
if (debug)
state->watch_stereo = 0;
if (debug)
} else {
/* triggered autodetect */
for (;;) {
} else {
/* triggered autodetect */
for (;;) {
- if (msp_sleep(state,100))
+ if (msp_sleep(state, 100))
goto restart;
/* check results */
goto restart;
/* check results */
int has_ntsc_jp_d_k3;
int has_scart4;
int has_scart23_in_scart2_out;
int has_ntsc_jp_d_k3;
int has_scart4;
int has_scart23_in_scart2_out;
+ int has_scart2_out_volume;
int has_subwoofer;
int has_sound_processing;
int has_virtual_dolby_surround;
int has_subwoofer;
int has_sound_processing;
int has_virtual_dolby_surround;
int opmode;
int mode;
v4l2_std_id std;
int opmode;
int mode;
v4l2_std_id std;
int nicam_on;
int acb;
int in_scart;
int nicam_on;
int acb;
int in_scart;
int audmode;
int rxsubchans;
int audmode;
int rxsubchans;
- int muted;
- int volume, balance;
+ int volume, muted;
+ int balance, loudness;
int bass, treble;
/* thread */
int bass, treble;
/* thread */