diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 386 |
1 files changed, 267 insertions, 119 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a010d704e0e2..d0d5ad8beac5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -114,6 +114,9 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format); + void (*pin_cvt_fixup)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid); }; struct hdmi_pcm { @@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (!channels) return; - if (is_haswell_plus(codec)) + /* some HW (e.g. HSW+) needs reprogramming the amp at each time */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); @@ -864,9 +868,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, struct hdmi_spec *spec = codec->spec; int err; - if (is_haswell_plus(codec)) - haswell_verify_D0(codec, cvt_nid, pin_nid); - err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format)); if (err) { @@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, * of the pin. */ static int hdmi_choose_cvt(struct hda_codec *codec, - int pin_idx, int *cvt_id, int *mux_id) + int pin_idx, int *cvt_id) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; @@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec, if (cvt_id) *cvt_id = cvt_idx; - if (mux_id) - *mux_id = mux_idx; return 0; } @@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, int mux_idx; struct hdmi_spec *spec = codec->spec; - if (!is_haswell_plus(codec) && !is_valleyview_plus(codec)) - return; - /* On Intel platform, the mapping of converter nid to * mux index of the pins are always the same. * The pin nid may be 0, this means all pins will not @@ -1032,6 +1028,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, intel_not_share_assigned_cvt(codec, pin_nid, mux_idx); } +/* skeleton caller of pin_cvt_fixup ops */ +static void pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->ops.pin_cvt_fixup) + spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid); +} + /* called in hdmi_pcm_open when no pin is assigned to the PCM * in dyn_pcm_assign mode. */ @@ -1049,7 +1056,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, if (pcm_idx < 0) return -EINVAL; - err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); + err = hdmi_choose_cvt(codec, -1, &cvt_idx); if (err) return err; @@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, per_cvt->assigned = 1; hinfo->nid = per_cvt->cvt_nid; - intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); + pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid); set_bit(pcm_idx, &spec->pcm_in_use); /* todo: setup spdif ctls assign */ @@ -1089,7 +1096,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, { struct hdmi_spec *spec = codec->spec; struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, pcm_idx, mux_idx = 0; + int pin_idx, cvt_idx, pcm_idx; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; struct hdmi_spec_per_cvt *per_cvt = NULL; @@ -1118,7 +1125,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, } } - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx); if (err < 0) { mutex_unlock(&spec->pcm_lock); return err; @@ -1135,11 +1142,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, AC_VERB_SET_CONNECT_SEL, - mux_idx); + per_pin->mux_idx); /* configure unused pins to choose other converters */ - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); + pin_cvt_fixup(codec, per_pin, 0); snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); @@ -1372,12 +1378,7 @@ static void update_eld(struct hda_codec *codec, * and this can make HW reset converter selection on a pin. */ if (eld->eld_valid && !old_eld_valid && per_pin->setup) { - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, - per_pin->mux_idx); - } - + pin_cvt_fixup(codec, per_pin, 0); hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } @@ -1484,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, mutex_lock(&per_pin->lock); eld->monitor_present = false; - size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid, + size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, &eld->monitor_present, eld->eld_buffer, ELD_MAX_SIZE); if (size > 0) { @@ -1711,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, * skip pin setup and return 0 to make audio playback * be ongoing */ - intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + pin_cvt_fixup(codec, NULL, cvt_nid); snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); mutex_unlock(&spec->pcm_lock); @@ -1724,23 +1725,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, } per_pin = get_pin(spec, pin_idx); pin_nid = per_pin->pin_nid; - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { - /* Verify pin:cvt selections to avoid silent audio after S3. - * After S3, the audio driver restores pin:cvt selections - * but this can happen before gfx is ready and such selection - * is overlooked by HW. Thus multiple pins can share a same - * default convertor and mute control will affect each other, - * which can cause a resumed audio playback become silent - * after S3. - */ - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx); - } + + /* Verify pin:cvt selections to avoid silent audio after S3. + * After S3, the audio driver restores pin:cvt selections + * but this can happen before gfx is ready and such selection + * is overlooked by HW. Thus multiple pins can share a same + * default convertor and mute control will affect each other, + * which can cause a resumed audio playback become silent + * after S3. + */ + pin_cvt_fixup(codec, per_pin, 0); /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Todo: add DP1.2 MST audio support later */ if (codec_has_acomp(codec)) - snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate); + snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate); non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); mutex_lock(&per_pin->lock); @@ -1837,6 +1836,18 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; +static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + if (!per_pin) + return 0; + + return per_pin->sink_eld.info.spk_alloc; +} + static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { @@ -2075,6 +2086,20 @@ static void hdmi_array_free(struct hdmi_spec *spec) snd_array_free(&spec->cvts); } +static void generic_spec_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec) { + if (spec->i915_bound) + snd_hdac_i915_exit(&codec->bus->core); + hdmi_array_free(spec); + kfree(spec); + codec->spec = NULL; + } + codec->dp_mst = false; +} + static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2099,10 +2124,7 @@ static void generic_hdmi_free(struct hda_codec *codec) spec->pcm_rec[pcm_idx].jack = NULL; } - if (spec->i915_bound) - snd_hdac_i915_exit(&codec->bus->core); - hdmi_array_free(spec); - kfree(spec); + generic_spec_free(codec); } #ifdef CONFIG_PM @@ -2140,6 +2162,55 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { .setup_stream = hdmi_setup_stream, }; +/* allocate codec->spec and assign/initialize generic parser ops */ +static int alloc_generic_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + spec->ops = generic_standard_hdmi_ops; + mutex_init(&spec->pcm_lock); + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc, + + codec->spec = spec; + hdmi_array_init(spec, 4); + + codec->patch_ops = generic_hdmi_patch_ops; + + return 0; +} + +/* generic HDMI parser */ +static int patch_generic_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; + } + + generic_hdmi_init_per_pins(codec); + return 0; +} + +/* + * Intel codec parsers and helpers + */ + static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2217,12 +2288,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, static void intel_pin_eld_notify(void *audio_ptr, int port) { struct hda_codec *codec = audio_ptr; - int pin_nid = port + 0x04; + int pin_nid; /* we assume only from port-B to port-D */ if (port < 1 || port > 3) return; + switch (codec->core.vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + pin_nid = port + 0x03; + break; + default: + pin_nid = port + 0x04; + break; + } + /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ @@ -2236,93 +2318,159 @@ static void intel_pin_eld_notify(void *audio_ptr, int port) check_presence_and_report(codec, pin_nid); } -static int patch_generic_hdmi(struct hda_codec *codec) +/* register i915 component pin_eld_notify callback */ +static void register_i915_notifier(struct hda_codec *codec) { - struct hdmi_spec *spec; + struct hdmi_spec *spec = codec->spec; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; + spec->use_acomp_notifier = true; + spec->i915_audio_ops.audio_ptr = codec; + /* intel_audio_codec_enable() or intel_audio_codec_disable() + * will call pin_eld_notify with using audio_ptr pointer + * We need make sure audio_ptr is really setup + */ + wmb(); + spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; + snd_hdac_i915_register_notifier(&spec->i915_audio_ops); +} - spec->ops = generic_standard_hdmi_ops; - mutex_init(&spec->pcm_lock); - snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); +/* setup_stream ops override for HSW+ */ +static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + haswell_verify_D0(codec, cvt_nid, pin_nid); + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); +} - spec->chmap.ops.get_chmap = hdmi_get_chmap; - spec->chmap.ops.set_chmap = hdmi_set_chmap; - spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; +/* pin_cvt_fixup ops override for HSW+ and VLV+ */ +static void i915_pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + if (per_pin) { + intel_verify_pin_cvt_connect(codec, per_pin); + intel_not_share_assigned_cvt(codec, per_pin->pin_nid, + per_pin->mux_idx); + } else { + intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + } +} - codec->spec = spec; - hdmi_array_init(spec, 4); +/* Intel Haswell and onwards; audio component with eld notifier */ +static int patch_i915_hsw_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; -#ifdef CONFIG_SND_HDA_I915 - /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */ - if ((codec->core.vendor_id >> 16) == 0x8086 && - is_haswell_plus(codec)) { -#if 0 - /* on-demand binding leads to an unbalanced refcount when - * both i915 and hda drivers are probed concurrently; - * disabled temporarily for now - */ - if (!codec->bus->core.audio_component) - if (!snd_hdac_i915_init(&codec->bus->core)) - spec->i915_bound = true; -#endif - /* use i915 audio component notifier for hotplug */ - if (codec->bus->core.audio_component) - spec->use_acomp_notifier = true; + /* HSW+ requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + return -ENODEV; } -#endif - if (is_haswell_plus(codec)) { - intel_haswell_enable_all_pins(codec, true); - intel_haswell_fixup_enable_dp12(codec); - } + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; - /* For Valleyview/Cherryview, only the display codec is in the display - * power well and can use link_power ops to request/release the power. - * For Haswell/Broadwell, the controller is also in the power well and + intel_haswell_enable_all_pins(codec, true); + intel_haswell_fixup_enable_dp12(codec); + + /* For Haswell/Broadwell, the controller is also in the power well and * can cover the codec power request, and so need not set this flag. - * For previous platforms, there is no such power well feature. */ - if (is_valleyview_plus(codec) || is_skylake(codec) || - is_broxton(codec)) + if (!is_haswell(codec) && !is_broadwell(codec)) codec->core.link_power_control = 1; - if (hdmi_parse_codec(codec) < 0) { - if (spec->i915_bound) - snd_hdac_i915_exit(&codec->bus->core); - codec->spec = NULL; - kfree(spec); - return -EINVAL; + codec->patch_ops.set_power_state = haswell_set_power_state; + codec->dp_mst = true; + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + spec->ops.setup_stream = i915_hsw_setup_stream; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; } - codec->patch_ops = generic_hdmi_patch_ops; - if (is_haswell_plus(codec)) { - codec->patch_ops.set_power_state = haswell_set_power_state; - codec->dp_mst = true; + + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel Baytrail and Braswell; with eld notifier */ +static int patch_i915_byt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + /* requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + return -ENODEV; } - /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */ - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) - codec->auto_runtime_pm = 1; + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; - generic_hdmi_init_per_pins(codec); + /* For Valleyview/Cherryview, only the display codec is in the display + * power well and can use link_power ops to request/release the power. + */ + codec->core.link_power_control = 1; + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; - if (codec_has_acomp(codec)) { - codec->depop_delay = 0; - spec->i915_audio_ops.audio_ptr = codec; - /* intel_audio_codec_enable() or intel_audio_codec_disable() - * will call pin_eld_notify with using audio_ptr pointer - * We need make sure audio_ptr is really setup - */ - wmb(); - spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; - snd_hdac_i915_register_notifier(&spec->i915_audio_ops); + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; } - WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ +static int patch_i915_cpt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + /* no i915 component should have been bound before this */ + if (WARN_ON(codec->bus->core.audio_component)) + return -EBUSY; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; + + /* Try to bind with i915 now */ + err = snd_hdac_i915_init(&codec->bus->core); + if (err < 0) + goto error; + spec->i915_bound = true; + + err = hdmi_parse_codec(codec); + if (err < 0) + goto error; + + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); return 0; + + error: + generic_spec_free(codec); + return err; } /* @@ -3492,21 +3640,21 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), +HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), /* special ID for generic HDMI */ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), |