diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 128 |
1 files changed, 100 insertions, 28 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ccd1df059654..1e4a4b83fbf6 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -78,6 +78,7 @@ struct hdmi_spec_per_pin { int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ + bool silent_stream; int channels; /* current number of channels */ bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ @@ -252,7 +253,7 @@ static int pin_id_to_pin_index(struct hda_codec *codec, return pin_idx; } - codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid); + codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); return -EINVAL; } @@ -312,7 +313,7 @@ static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid) return cvt_idx; - codec_warn(codec, "HDMI: cvt nid %d not registered\n", cvt_nid); + codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid); return -EINVAL; } @@ -637,11 +638,11 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, u8 val; int i; + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) != AC_DIPXMIT_BEST) return false; - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < size; i++) { val = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_DATA, 0); @@ -686,8 +687,7 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, dp_ai->CC02_CT47 = active_channels - 1; dp_ai->CA = ca; } else { - codec_dbg(codec, "HDMI: unknown connection type at pin %d\n", - pin_nid); + codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid); return; } @@ -700,10 +700,8 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, */ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, sizeof(ai))) { - codec_dbg(codec, - "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n", - pin_nid, - active_channels, ca); + codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n", + __func__, pin_nid, active_channels, ca); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); @@ -795,7 +793,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, jack->jack_dirty = 1; codec_dbg(codec, - "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", + "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); @@ -873,7 +871,7 @@ static void haswell_verify_D0(struct hda_codec *codec, msleep(40); pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; - codec_dbg(codec, "Haswell HDMI audio: Power for pin 0x%x is now D%d\n", nid, pwr); + codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); } } @@ -979,6 +977,13 @@ static int hdmi_choose_cvt(struct hda_codec *codec, else per_pin = get_pin(spec, pin_idx); + if (per_pin && per_pin->silent_stream) { + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + if (cvt_id) + *cvt_id = cvt_idx; + return 0; + } + /* Dynamically assign converter to stream */ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { per_cvt = get_cvt(spec, cvt_idx); @@ -1113,8 +1118,8 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, per_cvt = get_cvt(spec, cvt_idx); if (!per_cvt->assigned) { codec_dbg(codec, - "choose cvt %d for pin nid %d\n", - cvt_idx, nid); + "choose cvt %d for pin NID 0x%x\n", + cvt_idx, nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, cvt_idx); @@ -1312,7 +1317,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { codec_warn(codec, - "HDMI: pin %d wcaps %#x does not support connection list\n", + "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n", pin_nid, get_wcaps(codec, pin_nid)); return -EINVAL; } @@ -1627,7 +1632,7 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, eld->eld_valid = false; codec_dbg(codec, - "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n", codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); if (eld->eld_valid) { @@ -1642,30 +1647,95 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, snd_hda_power_down_pm(codec); } +#define I915_SILENT_RATE 48000 +#define I915_SILENT_CHANNELS 2 +#define I915_SILENT_FORMAT SNDRV_PCM_FORMAT_S16_LE +#define I915_SILENT_FORMAT_BITS 16 +#define I915_SILENT_FMT_MASK 0xf + static void silent_stream_enable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) + struct hdmi_spec_per_pin *per_pin) { - unsigned int newval, oldval; - - codec_dbg(codec, "hdmi: enabling silent stream for NID %d\n", - per_pin->pin_nid); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx, pin_idx, err; + unsigned int format; mutex_lock(&per_pin->lock); - if (!per_pin->channels) - per_pin->channels = 2; + if (per_pin->setup) { + codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); + goto unlock_out; + } - oldval = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_CONV, 0); - newval = (oldval & 0xF0) | 0xF; - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, newval); + pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id); + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx); + if (err) { + codec_err(codec, "hdmi: no free converter to enable silent mode\n"); + goto unlock_out; + } + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 1; + per_pin->cvt_nid = per_cvt->cvt_nid; + per_pin->silent_stream = true; + + codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_cvt->cvt_nid); + + snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + per_pin->mux_idx); + + /* configure unused pins to choose other converters */ + pin_cvt_fixup(codec, per_pin, 0); + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, + I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + unlock_out: mutex_unlock(&per_pin->lock); } +static void silent_stream_disable(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx; + + mutex_lock(&per_pin->lock); + if (!per_pin->silent_stream) + goto unlock_out; + + codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_pin->cvt_nid); + + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 0; + } + + per_pin->cvt_nid = 0; + per_pin->silent_stream = false; + + unlock_out: + mutex_unlock(&spec->pcm_lock); +} + /* update ELD and jack state via audio component */ static void sync_eld_via_acomp(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) @@ -1701,6 +1771,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, pm_ret); silent_stream_enable(codec, per_pin); } else if (monitor_prev && !monitor_next) { + silent_stream_disable(codec, per_pin); pm_ret = snd_hda_power_down_pm(codec); if (pm_ret < 0) codec_err(codec, @@ -2721,7 +2792,7 @@ static int intel_pin2port(void *audio_ptr, int pin_nid) return i; } - codec_info(codec, "Can't find the HDMI/DP port for pin %d\n", pin_nid); + codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); return -1; } @@ -4274,6 +4345,7 @@ HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), |