aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-09-02 06:33:02 -0400
committerTakashi Iwai <tiwai@suse.de>2013-09-03 02:26:54 -0400
commitb054087dbacee30a9dddaef2c9a96312146be04e (patch)
treebb876655c7e4b42c5ae6788b588c84ae123abc61 /sound
parentc8ead4154af3d43635f63a708a2f369c7cce09e7 (diff)
ALSA: hda - Re-setup HDMI pin and audio infoframe on stream switches
When the transcoder:port mapping on Haswell HDMI/DP audio is changed during the stream playback, the sound gets lost. Typically this problem is seen when the user switches the graphics mode from eDP+DP to DP-only configuration, where CRTC 1 is used for DP in the former while CRTC 0 is used for the latter. The graphics controller notifies the change via the normal ELD update procedure, so we get the intrinsic event. For enabling the sound again, the HDMI audio driver needs to reset the pin and set up the audio infoframe again. This patch achieves it by: - keep the current status of channels and info frame setup in per_pin struct, - check the reconnection in the intrinsic event handler, - reset the pin and the re-invoke hdmi_setup_audio_infoframe() accordingly. The hdmi_setup_audio_infoframe() function has been changed, too, so that it can be invoked without passing the substream instance. The patch is mostly based on the work by Mengdong Lin. Cc: Mengdong Lin <mengdong.lin@intel.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_hdmi.c41
1 files changed, 31 insertions, 10 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index b83b14fa1d29..22b50899b151 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -67,6 +67,8 @@ struct hdmi_spec_per_pin {
67 struct delayed_work work; 67 struct delayed_work work;
68 struct snd_kcontrol *eld_ctl; 68 struct snd_kcontrol *eld_ctl;
69 int repoll_count; 69 int repoll_count;
70 bool setup; /* the stream has been set up by prepare callback */
71 int channels; /* current number of channels */
70 bool non_pcm; 72 bool non_pcm;
71 bool chmap_set; /* channel-map override by ALSA API? */ 73 bool chmap_set; /* channel-map override by ALSA API? */
72 unsigned char chmap[8]; /* ALSA API channel-map */ 74 unsigned char chmap[8]; /* ALSA API channel-map */
@@ -879,18 +881,19 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
879 return true; 881 return true;
880} 882}
881 883
882static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, 884static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
883 bool non_pcm, 885 struct hdmi_spec_per_pin *per_pin,
884 struct snd_pcm_substream *substream) 886 bool non_pcm)
885{ 887{
886 struct hdmi_spec *spec = codec->spec;
887 struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
888 hda_nid_t pin_nid = per_pin->pin_nid; 888 hda_nid_t pin_nid = per_pin->pin_nid;
889 int channels = substream->runtime->channels; 889 int channels = per_pin->channels;
890 struct hdmi_eld *eld; 890 struct hdmi_eld *eld;
891 int ca; 891 int ca;
892 union audio_infoframe ai; 892 union audio_infoframe ai;
893 893
894 if (!channels)
895 return;
896
894 eld = &per_pin->sink_eld; 897 eld = &per_pin->sink_eld;
895 if (!eld->monitor_present) 898 if (!eld->monitor_present)
896 return; 899 return;
@@ -1341,6 +1344,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
1341 eld_changed = true; 1344 eld_changed = true;
1342 } 1345 }
1343 if (update_eld) { 1346 if (update_eld) {
1347 bool old_eld_valid = pin_eld->eld_valid;
1344 pin_eld->eld_valid = eld->eld_valid; 1348 pin_eld->eld_valid = eld->eld_valid;
1345 eld_changed = pin_eld->eld_size != eld->eld_size || 1349 eld_changed = pin_eld->eld_size != eld->eld_size ||
1346 memcmp(pin_eld->eld_buffer, eld->eld_buffer, 1350 memcmp(pin_eld->eld_buffer, eld->eld_buffer,
@@ -1350,6 +1354,18 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
1350 eld->eld_size); 1354 eld->eld_size);
1351 pin_eld->eld_size = eld->eld_size; 1355 pin_eld->eld_size = eld->eld_size;
1352 pin_eld->info = eld->info; 1356 pin_eld->info = eld->info;
1357
1358 /* Haswell-specific workaround: re-setup when the transcoder is
1359 * changed during the stream playback
1360 */
1361 if (codec->vendor_id == 0x80862807 &&
1362 eld->eld_valid && !old_eld_valid && per_pin->setup) {
1363 snd_hda_codec_write(codec, pin_nid, 0,
1364 AC_VERB_SET_AMP_GAIN_MUTE,
1365 AMP_OUT_UNMUTE);
1366 hdmi_setup_audio_infoframe(codec, per_pin,
1367 per_pin->non_pcm);
1368 }
1353 } 1369 }
1354 mutex_unlock(&pin_eld->lock); 1370 mutex_unlock(&pin_eld->lock);
1355 1371
@@ -1522,14 +1538,17 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
1522 hda_nid_t cvt_nid = hinfo->nid; 1538 hda_nid_t cvt_nid = hinfo->nid;
1523 struct hdmi_spec *spec = codec->spec; 1539 struct hdmi_spec *spec = codec->spec;
1524 int pin_idx = hinfo_to_pin_index(spec, hinfo); 1540 int pin_idx = hinfo_to_pin_index(spec, hinfo);
1525 hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid; 1541 struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
1542 hda_nid_t pin_nid = per_pin->pin_nid;
1526 bool non_pcm; 1543 bool non_pcm;
1527 1544
1528 non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); 1545 non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
1546 per_pin->channels = substream->runtime->channels;
1547 per_pin->setup = true;
1529 1548
1530 hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); 1549 hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
1531 1550
1532 hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream); 1551 hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
1533 1552
1534 return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); 1553 return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
1535} 1554}
@@ -1569,6 +1588,9 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
1569 snd_hda_spdif_ctls_unassign(codec, pin_idx); 1588 snd_hda_spdif_ctls_unassign(codec, pin_idx);
1570 per_pin->chmap_set = false; 1589 per_pin->chmap_set = false;
1571 memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); 1590 memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
1591
1592 per_pin->setup = false;
1593 per_pin->channels = 0;
1572 } 1594 }
1573 1595
1574 return 0; 1596 return 0;
@@ -1704,8 +1726,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
1704 per_pin->chmap_set = true; 1726 per_pin->chmap_set = true;
1705 memcpy(per_pin->chmap, chmap, sizeof(chmap)); 1727 memcpy(per_pin->chmap, chmap, sizeof(chmap));
1706 if (prepared) 1728 if (prepared)
1707 hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, 1729 hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
1708 substream);
1709 1730
1710 return 0; 1731 return 0;
1711} 1732}