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
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);
hda_nid_t nid = kcontrol->private_value;
unsigned int sbits;
unsigned short val;
int change;
unsigned short val = 0;
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)
val = 1 << 6;
val |= 1 << 6;
if (sbits & IEC958_AES0_NONAUDIO)
val |= 1 << 5;
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)
val |= 1 << 3;
} 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)
val |= 1 << 3;
if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
val |= 1 << 4;
sbits |= ucontrol->value.iec958.status[1] << 8;
if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
val |= 1 << 7;
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;
codec->spdif_status = sbits;
static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
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_2, val >> 8);
}
......@@ -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);
ucontrol->value.integer.value[0] = codec->spdif_status & 1;
ucontrol->value.integer.value[0] = codec->spdif_ctls & 1;
return 0;
}
......@@ -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);
hda_nid_t nid = kcontrol->private_value;
unsigned int sbits;
unsigned short val;
int change;
down(&codec->spdif_mutex);
sbits = codec->spdif_status & ~1;
val = codec->spdif_ctls & ~1;
if (ucontrol->value.integer.value[0])
sbits |= 1;
change = codec->spdif_status != sbits;
if (change) {
codec->spdif_status = sbits;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, sbits & 0xff);
val |= 1;
change = codec->spdif_ctls != val;
if (change || codec->in_resume) {
codec->spdif_ctls = val;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
}
up(&codec->spdif_mutex);
return change;
......@@ -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)
return err;
}
#if 0
/* not enabled, consumer, audio, no emphasis, original, PCM coder */
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
codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
return 0;
}
......@@ -1213,7 +1240,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format)
{
int i;
unsigned int val = 0, rate;
unsigned int val = 0, rate, stream;
if (nid != codec->afg &&
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,
return 0;
}
rate = format & 0xffff;
rate = format & 0xff00;
for (i = 0; rate_bits[i][0]; i++)
if (rate_bits[i][2] == rate) {
if (val & (1 << i))
......@@ -1237,15 +1264,15 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
if (! rate_bits[i][0])
return 0;
val = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (val == -1)
stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (stream == -1)
return 0;
if (! val && nid != codec->afg)
val = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (! val || val == -1)
if (! stream && nid != codec->afg)
stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
if (! stream || stream == -1)
return 0;
if (val & AC_SUPFMT_PCM) {
if (stream & AC_SUPFMT_PCM) {
switch (format & 0xf0) {
case 0x00:
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
down(&codec->spdif_mutex);
if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
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;
/* setup digital receiver */
snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
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 {
mout->dig_out_used = 0;
snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
......
......@@ -529,7 +529,8 @@ struct hda_codec {
struct hda_amp_info amp_info[128]; /* big enough? */
struct semaphore spdif_mutex;
unsigned int spdif_status;
unsigned int spdif_status; /* IEC958 status bits */
unsigned short spdif_ctls; /* SPDIF control bits */
};
/* 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