summaryrefslogtreecommitdiff
path: root/sound/pci/hda/patch_hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r--sound/pci/hda/patch_hdmi.c148
1 files changed, 137 insertions, 11 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 21edf7a619f0..9ea633fe9339 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -167,6 +167,7 @@ struct hdmi_spec {
struct hdmi_ops ops;
bool dyn_pin_out;
+ bool static_pcm_mapping;
/* hdmi interrupt trigger control flag for Nvidia codec */
bool hdmi_intr_trig_ctrl;
bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */
@@ -1525,13 +1526,16 @@ static void update_eld(struct hda_codec *codec,
*/
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
- if (eld->eld_valid) {
- hdmi_attach_hda_pcm(spec, per_pin);
- hdmi_pcm_setup_pin(spec, per_pin);
- } else {
- hdmi_pcm_reset_pin(spec, per_pin);
- hdmi_detach_hda_pcm(spec, per_pin);
+ if (!spec->static_pcm_mapping) {
+ if (eld->eld_valid) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ } else {
+ hdmi_pcm_reset_pin(spec, per_pin);
+ hdmi_detach_hda_pcm(spec, per_pin);
+ }
}
+
/* if pcm_idx == -1, it means this is in monitor connection event
* we can get the correct pcm_idx now.
*/
@@ -1738,6 +1742,7 @@ static void silent_stream_enable(struct hda_codec *codec,
switch (spec->silent_stream_type) {
case SILENT_STREAM_KAE:
+ silent_stream_enable_i915(codec, per_pin);
silent_stream_set_kae(codec, per_pin, true);
break;
case SILENT_STREAM_I915:
@@ -1975,6 +1980,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
static const struct snd_pci_quirk force_connect_list[] = {
SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1),
SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
{}
@@ -2279,8 +2286,8 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec;
int idx, pcm_num;
- /* limit the PCM devices to the codec converters */
- pcm_num = spec->num_cvts;
+ /* limit the PCM devices to the codec converters or available PINs */
+ pcm_num = min(spec->num_cvts, spec->num_pins);
codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
for (idx = 0; idx < pcm_num; idx++) {
@@ -2377,6 +2384,11 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ if (spec->static_pcm_mapping) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ }
+
pin_eld->eld_valid = false;
hdmi_present_sense(per_pin, 0);
}
@@ -2878,9 +2890,33 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
int format)
{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id);
+ struct hdmi_spec_per_pin *per_pin;
+ int res;
+
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
haswell_verify_D0(codec, cvt_nid, pin_nid);
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
- stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ silent_stream_set_kae(codec, per_pin, false);
+ /* wait for pending transfers in codec to clear */
+ usleep_range(100, 200);
+ }
+
+ res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ usleep_range(100, 200);
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+
+ return res;
}
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
@@ -2900,6 +2936,88 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
}
}
+#ifdef CONFIG_PM
+static int i915_adlp_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ bool silent_streams = false;
+ int pin_idx, res;
+
+ res = generic_hdmi_suspend(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin->silent_stream) {
+ silent_streams = true;
+ break;
+ }
+ }
+
+ if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) {
+ /*
+ * stream-id should remain programmed when codec goes
+ * to runtime suspend
+ */
+ codec->no_stream_clean_at_suspend = 1;
+
+ /*
+ * the system might go to S3, in which case keep-alive
+ * must be reprogrammed upon resume
+ */
+ codec->forced_resume = 1;
+
+ codec_dbg(codec, "HDMI: KAE active at suspend\n");
+ } else {
+ codec->no_stream_clean_at_suspend = 0;
+ codec->forced_resume = 0;
+ }
+
+ return res;
+}
+
+static int i915_adlp_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, res;
+
+ res = generic_hdmi_resume(codec);
+
+ /* KAE not programmed at suspend, nothing to do here */
+ if (!codec->no_stream_clean_at_suspend)
+ return res;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /*
+ * If system was in suspend with monitor connected,
+ * the codec setting may have been lost. Re-enable
+ * keep-alive.
+ */
+ if (per_pin->silent_stream) {
+ unsigned int param;
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_CONV, 0);
+ if (!param) {
+ codec_dbg(codec, "HDMI: KAE: restore stream id\n");
+ silent_stream_enable_i915(codec, per_pin);
+ }
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ if (!(param & (AC_DIG3_KAE << 16))) {
+ codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n");
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+ }
+ }
+
+ return res;
+}
+#endif
+
/* precondition and allocation for Intel codecs */
static int alloc_intel_hdmi(struct hda_codec *codec)
{
@@ -3030,8 +3148,14 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec)
if (!res) {
spec = codec->spec;
- if (spec->silent_stream_type)
+ if (spec->silent_stream_type) {
spec->silent_stream_type = SILENT_STREAM_KAE;
+
+#ifdef CONFIG_PM
+ codec->patch_ops.resume = i915_adlp_hdmi_resume;
+ codec->patch_ops.suspend = i915_adlp_hdmi_suspend;
+#endif
+ }
}
return res;
@@ -4305,6 +4429,8 @@ static int patch_atihdmi(struct hda_codec *codec)
spec = codec->spec;
+ spec->static_pcm_mapping = true;
+
spec->ops.pin_get_eld = atihdmi_pin_get_eld;
spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;