diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 122 |
1 files changed, 102 insertions, 20 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 807a2aa1ff38..b9af281b2ba4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -64,6 +64,9 @@ struct hdmi_spec_per_cvt { unsigned int maxbps; }; +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + struct hdmi_spec_per_pin { hda_nid_t pin_nid; int num_mux_nids; @@ -81,6 +84,7 @@ struct hdmi_spec_per_pin { struct hdmi_spec { int num_cvts; struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; + hda_nid_t cvt_nids[MAX_HDMI_CVTS]; int num_pins; struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; @@ -714,9 +718,10 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map) + int channels, unsigned char *map, + bool chmap_set) { - if (!non_pcm && map) { + if (!non_pcm && chmap_set) { hdmi_manual_setup_channel_mapping(codec, pin_nid, channels, map); } else { @@ -905,7 +910,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, pin_nid, channels); hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap); + channels, per_pin->chmap, + per_pin->chmap_set); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); @@ -915,7 +921,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, * accordingly */ if (per_pin->non_pcm != non_pcm) hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap); + channels, per_pin->chmap, + per_pin->chmap_set); } per_pin->non_pcm = non_pcm; @@ -1100,8 +1107,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) + !hinfo->rates || !hinfo->formats) { + per_cvt->assigned = 0; + hinfo->nid = 0; + snd_hda_spdif_ctls_unassign(codec, pin_idx); return -ENODEV; + } } /* Store the updated parameters */ @@ -1187,6 +1198,9 @@ static void hdmi_repoll_eld(struct work_struct *work) hdmi_present_sense(per_pin, per_pin->repoll_count); } +static void intel_haswell_fixup_connect_list(struct hda_codec *codec, + hda_nid_t nid); + static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; @@ -1206,6 +1220,9 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) return -E2BIG; + if (codec->vendor_id == 0x80862807) + intel_haswell_fixup_connect_list(codec, pin_nid); + pin_idx = spec->num_pins; per_pin = &spec->pins[pin_idx]; @@ -1253,7 +1270,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) if (err < 0) return err; - spec->num_cvts++; + spec->cvt_nids[spec->num_cvts++] = cvt_nid; return 0; } @@ -1681,30 +1698,92 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { .unsol_event = hdmi_unsol_event, }; -static void intel_haswell_fixup_connect_list(struct hda_codec *codec) + +static void intel_haswell_fixup_connect_list(struct hda_codec *codec, + hda_nid_t nid) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t conns[4]; + int nconns; + + nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns)); + if (nconns == spec->num_cvts && + !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t))) + return; + + /* override pins connection list */ + snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid); + snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids); +} + +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void intel_haswell_enable_all_pins(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { unsigned int vendor_param; - hda_nid_t list[3] = {0x2, 0x3, 0x4}; - vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || vendor_param & 0x02) + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) return; - /* enable DP1.2 mode */ - vendor_param |= 0x02; - snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param); + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + + snd_hda_codec_update_widgets(codec); + return; +} - vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || !(vendor_param & 0x02)) +static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) +{ + unsigned int vendor_param; + + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) return; - /* override 3 pins connection list */ - snd_hda_override_conn_list(codec, 0x05, 3, list); - snd_hda_override_conn_list(codec, 0x06, 3, list); - snd_hda_override_conn_list(codec, 0x07, 3, list); + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); } + +/* available models for fixup */ +enum { + INTEL_HASWELL, +}; + +static const struct hda_model_fixup hdmi_models[] = { + {.id = INTEL_HASWELL, .name = "Haswell"}, + {} +}; + +static const struct snd_pci_quirk hdmi_fixup_tbl[] = { + SND_PCI_QUIRK(0x8086, 0x2010, "Haswell", INTEL_HASWELL), + {} /* terminator */ +}; + +static const struct hda_fixup hdmi_fixups[] = { + [INTEL_HASWELL] = { + .type = HDA_FIXUP_FUNC, + .v.func = intel_haswell_enable_all_pins, + }, +}; + + static int patch_generic_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -1715,8 +1794,11 @@ static int patch_generic_hdmi(struct hda_codec *codec) codec->spec = spec; + snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (codec->vendor_id == 0x80862807) - intel_haswell_fixup_connect_list(codec); + intel_haswell_fixup_enable_dp12(codec); if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; |