Commit 33e44d91 authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] Fix SPDIF output

HDA Codec driver
Fixed SPDIF output (over multi-out).
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent ac8e27f5
...@@ -845,44 +845,75 @@ static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu ...@@ -845,44 +845,75 @@ static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu
return 0; return 0;
} }
static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) /* convert from SPDIF status bits to HDA SPDIF bits
* bit 0 (DigEn) is always set zero (to be filled later)
*/
static unsigned short convert_from_spdif_status(unsigned int sbits)
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned short val = 0;
hda_nid_t nid = kcontrol->private_value;
unsigned int sbits;
unsigned short val;
int change;
val = 0;
down(&codec->spdif_mutex);
sbits = codec->spdif_status & 1;
sbits |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|
IEC958_AES0_NONAUDIO);
if (sbits & IEC958_AES0_PROFESSIONAL) if (sbits & IEC958_AES0_PROFESSIONAL)
val = 1 << 6; val |= 1 << 6;
if (sbits & IEC958_AES0_NONAUDIO) if (sbits & IEC958_AES0_NONAUDIO)
val |= 1 << 5; val |= 1 << 5;
if (sbits & IEC958_AES0_PROFESSIONAL) { if (sbits & IEC958_AES0_PROFESSIONAL) {
sbits |= ucontrol->value.iec958.status[0] & IEC958_AES0_PRO_EMPHASIS;
if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
val |= 1 << 3; val |= 1 << 3;
} else { } else {
sbits |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS|
IEC958_AES0_CON_NOT_COPYRIGHT);
if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
val |= 1 << 3; val |= 1 << 3;
if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
val |= 1 << 4; val |= 1 << 4;
sbits |= ucontrol->value.iec958.status[1] << 8;
if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
val |= 1 << 7; val |= 1 << 7;
val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
} }
return val;
}
/* convert to SPDIF status bits from HDA SPDIF bits
*/
static unsigned int convert_to_spdif_status(unsigned short val)
{
unsigned int sbits = 0;
if (val & (1 << 5))
sbits |= IEC958_AES0_NONAUDIO;
if (val & (1 << 6))
sbits |= IEC958_AES0_PROFESSIONAL;
if (sbits & IEC958_AES0_PROFESSIONAL) {
if (sbits & (1 << 3))
sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
} else {
if (val & (1 << 3))
sbits |= IEC958_AES0_CON_EMPHASIS_5015;
if (! (val & (1 << 4)))
sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
if (val & (1 << 7))
sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
sbits |= val & (0x7f << 8);
}
return sbits;
}
change = codec->spdif_status != sbits; static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
codec->spdif_status = sbits; {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
unsigned short val;
int change;
if (change) { down(&codec->spdif_mutex);
codec->spdif_status = ucontrol->value.iec958.status[0] |
((unsigned int)ucontrol->value.iec958.status[1] << 8) |
((unsigned int)ucontrol->value.iec958.status[2] << 16) |
((unsigned int)ucontrol->value.iec958.status[3] << 24);
val = convert_from_spdif_status(codec->spdif_status);
val |= codec->spdif_ctls & 1;
change = codec->spdif_ctls != val;
codec->spdif_ctls = val;
if (change || codec->in_resume) {
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8);
} }
...@@ -904,7 +935,7 @@ static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_v ...@@ -904,7 +935,7 @@ static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = codec->spdif_status & 1; ucontrol->value.integer.value[0] = codec->spdif_ctls & 1;
return 0; return 0;
} }
...@@ -912,17 +943,17 @@ static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v ...@@ -912,17 +943,17 @@ static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value; hda_nid_t nid = kcontrol->private_value;
unsigned int sbits; unsigned short val;
int change; int change;
down(&codec->spdif_mutex); down(&codec->spdif_mutex);
sbits = codec->spdif_status & ~1; val = codec->spdif_ctls & ~1;
if (ucontrol->value.integer.value[0]) if (ucontrol->value.integer.value[0])
sbits |= 1; val |= 1;
change = codec->spdif_status != sbits; change = codec->spdif_ctls != val;
if (change) { if (change || codec->in_resume) {
codec->spdif_status = sbits; codec->spdif_ctls = val;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, sbits & 0xff); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
} }
up(&codec->spdif_mutex); up(&codec->spdif_mutex);
return change; return change;
...@@ -982,12 +1013,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) ...@@ -982,12 +1013,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0) if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
return err; return err;
} }
#if 0 codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
/* not enabled, consumer, audio, no emphasis, original, PCM coder */ codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
codec->spdif_status = (1 << 7) | (0x02 << 8);
#else
codec->spdif_status = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
#endif
return 0; return 0;
} }
...@@ -1213,7 +1240,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, ...@@ -1213,7 +1240,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format) unsigned int format)
{ {
int i; int i;
unsigned int val = 0, rate; unsigned int val = 0, rate, stream;
if (nid != codec->afg && if (nid != codec->afg &&
snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) { snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
...@@ -1227,7 +1254,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, ...@@ -1227,7 +1254,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
return 0; return 0;
} }
rate = format & 0xffff; rate = format & 0xff00;
for (i = 0; rate_bits[i][0]; i++) for (i = 0; rate_bits[i][0]; i++)
if (rate_bits[i][2] == rate) { if (rate_bits[i][2] == rate) {
if (val & (1 << i)) if (val & (1 << i))
...@@ -1237,15 +1264,15 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, ...@@ -1237,15 +1264,15 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
if (! rate_bits[i][0]) if (! rate_bits[i][0])
return 0; return 0;
val = snd_hda_param_read(codec, nid, AC_PAR_STREAM); stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (val == -1) if (stream == -1)
return 0; return 0;
if (! val && nid != codec->afg) if (! stream && nid != codec->afg)
val = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (! val || val == -1) if (! stream || stream == -1)
return 0; return 0;
if (val & AC_SUPFMT_PCM) { if (stream & AC_SUPFMT_PCM) {
switch (format & 0xf0) { switch (format & 0xf0) {
case 0x00: case 0x00:
if (! (val & AC_SUPPCM_BITS_8)) if (! (val & AC_SUPPCM_BITS_8))
...@@ -1542,22 +1569,12 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o ...@@ -1542,22 +1569,12 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
down(&codec->spdif_mutex); down(&codec->spdif_mutex);
if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
if (chs == 2 && if (chs == 2 &&
snd_hda_is_supported_format(codec, mout->dig_out_nid, format)) { snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
mout->dig_out_used = HDA_DIG_ANALOG_DUP; mout->dig_out_used = HDA_DIG_ANALOG_DUP;
/* setup digital receiver */ /* setup digital receiver */
snd_hda_codec_setup_stream(codec, mout->dig_out_nid, snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
stream_tag, 0, format); stream_tag, 0, format);
if (codec->spdif_status & AC_DIG1_NONAUDIO) {
/* non-audio SPDIF out, turning off all DACs */
for (i = 0; i < mout->num_dacs; i++)
snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
if (mout->hp_nid)
snd_hda_codec_setup_stream(codec, mout->hp_nid,0, 0, 0);
up(&codec->spdif_mutex);
return 0;
}
} else { } else {
mout->dig_out_used = 0; mout->dig_out_used = 0;
snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
......
...@@ -529,7 +529,8 @@ struct hda_codec { ...@@ -529,7 +529,8 @@ struct hda_codec {
struct hda_amp_info amp_info[128]; /* big enough? */ struct hda_amp_info amp_info[128]; /* big enough? */
struct semaphore spdif_mutex; struct semaphore spdif_mutex;
unsigned int spdif_status; unsigned int spdif_status; /* IEC958 status bits */
unsigned short spdif_ctls; /* SPDIF control bits */
}; };
/* direction */ /* direction */
......
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