/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. * Otherwise you may get severe h/w communication errors.
*/ staticvoid haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsignedint power_state)
{ /* check codec->spec: it can be called before the probe gets called */ if (codec->spec) { if (power_state == AC_PWRST_D0) {
intel_haswell_enable_all_pins(codec, false);
intel_haswell_fixup_enable_dp12(codec);
}
}
/* There is a fixed mapping between audio pin node and display port. * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: * Pin Widget 5 - PORT B (port = 1 in i915 driver) * Pin Widget 6 - PORT C (port = 2 in i915 driver) * Pin Widget 7 - PORT D (port = 3 in i915 driver) * * on VLV, ILK: * Pin Widget 4 - PORT B (port = 1 in i915 driver) * Pin Widget 5 - PORT C (port = 2 in i915 driver) * Pin Widget 6 - PORT D (port = 3 in i915 driver)
*/ staticint intel_base_nid(struct hda_codec *codec)
{ switch (codec->core.vendor_id) { case 0x80860054: /* ILK */ case 0x80862804: /* ILK */ case 0x80862882: /* VLV */ return 4; default: return 5;
}
}
staticint intel_pin2port(void *audio_ptr, int pin_nid)
{ struct hda_codec *codec = audio_ptr; struct hdmi_spec *spec = codec->spec; int base_nid, i;
/* * looking for the pin number in the mapping table and return * the index which indicate the port number
*/ for (i = 0; i < spec->port_num; i++) { if (pin_nid == spec->port_map[i]) return i;
}
codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); return -1;
}
if (!spec->port_num) { /* we assume only from port-B to port-D */ if (port < 1 || port > 3) return 0; return port + intel_base_nid(codec) - 1;
}
if (port < 0 || port >= spec->port_num) return 0; return spec->port_map[port];
}
staticvoid intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
{ struct hda_codec *codec = audio_ptr; int pin_nid; int dev_id = pipe;
pin_nid = intel_port2pin(codec, port); if (!pin_nid) return; /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume
*/ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) return;
spec->use_acomp_notifier = true;
spec->port2pin = intel_port2pin;
snd_hda_hdmi_setup_drm_audio_ops(codec, &intel_audio_ops);
snd_hdac_acomp_register_notifier(&codec->bus->core,
&spec->drm_audio_ops); /* no need for forcible resume for jack check thanks to notifier */
codec->relaxed_resume = 1;
}
/* For Haswell, the converter 1/2 may keep in D3 state after bootup, * thus pins could only choose converter 0 for use. Make sure the * converters are in correct power state
*/ if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
AC_PWRST_D0);
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 NID 0x%x is now D%d\n", nid, pwr);
}
}
/* Assure the pin select the right convetor */ staticvoid intel_verify_pin_cvt_connect(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin)
{
hda_nid_t pin_nid = per_pin->pin_nid; int mux_idx, curr;
/* get the mux index for the converter of the pins * converter's mux index is the same for all pins on Intel platform
*/ staticint intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
hda_nid_t cvt_nid)
{ int i;
for (i = 0; i < spec->num_cvts; i++) if (spec->cvt_nids[i] == cvt_nid) return i; return -EINVAL;
}
/* Intel HDMI workaround to fix audio routing issue: * For some Intel display codecs, pins share the same connection list. * So a conveter can be selected by multiple pins and playback on any of these * pins will generate sound on the external display, because audio flows from * the same converter to the display pipeline. Also muting one pin may make * other pins have no sound output. * So this function assures that an assigned converter for a pin is not selected * by any other pins.
*/ staticvoid intel_not_share_assigned_cvt(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id, int mux_idx)
{ struct hdmi_spec *spec = codec->spec;
hda_nid_t nid; int cvt_idx, curr; struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; int pin_idx;
/* configure the pins connections */ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { int dev_id_saved; int dev_num;
per_pin = get_pin(spec, pin_idx); /* * pin not connected to monitor * no need to operate on it
*/ if (!per_pin->pcm) continue;
if ((per_pin->pin_nid == pin_nid) &&
(per_pin->dev_id == dev_id)) continue;
/* * if per_pin->dev_id >= dev_num, * snd_hda_get_dev_select() will fail, * and the following operation is unpredictable. * So skip this situation.
*/
dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; if (per_pin->dev_id >= dev_num) continue;
nid = per_pin->pin_nid;
/* * Calling this function should not impact * on the device entry selection * So let's save the dev id for each pin, * and restore it when return
*/
dev_id_saved = snd_hda_get_dev_select(codec, nid);
snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
curr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_SEL, 0); if (curr != mux_idx) {
snd_hda_set_dev_select(codec, nid, dev_id_saved); continue;
}
/* choose an unassigned converter. The conveters in the * connection list are in the same order as in the codec.
*/ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
per_cvt = get_cvt(spec, cvt_idx); if (!per_cvt->assigned) {
codec_dbg(codec, "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); break;
}
}
snd_hda_set_dev_select(codec, nid, dev_id_saved);
}
}
/* A wrapper of intel_not_share_asigned_cvt() */ staticvoid intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
{ int mux_idx; struct hdmi_spec *spec = codec->spec;
/* 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 * share the converter.
*/
mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); if (mux_idx >= 0)
intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
}
/* setup_stream ops override for HSW+ */ staticint 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 system was in suspend with monitor connected, * the codec setting may have been lost. Re-enable * keep-alive.
*/ if (per_pin->silent_stream) { unsignedint param;
/* * Enable silent stream feature, if it is enabled via * module param or Kconfig option
*/ if (send_silent_stream)
spec->silent_stream_type = SILENT_STREAM_I915;
staticint probe_i915_glk_hdmi(struct hda_codec *codec)
{ /* * Silent stream calls audio component .get_power() from * .pin_eld_notify(). On GLK this will deadlock in i915 due * to the audio vs. CDCLK workaround.
*/ return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false);
}
staticint probe_i915_icl_hdmi(struct hda_codec *codec)
{ /* * pin to port mapping table where the value indicate the pin number and * the index indicate the port number.
*/ staticconstint map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb};
staticint probe_i915_tgl_hdmi(struct hda_codec *codec)
{ /* * pin to port mapping table where the value indicate the pin number and * the index indicate the port number.
*/ staticconstint map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
staticint probe_i915_adlp_hdmi(struct hda_codec *codec)
{ struct hdmi_spec *spec; int res;
res = probe_i915_tgl_hdmi(codec); if (!res) {
spec = codec->spec;
if (spec->silent_stream_type)
spec->silent_stream_type = SILENT_STREAM_KAE;
}
return res;
}
/* Intel Baytrail and Braswell; with eld notifier */ staticint probe_i915_byt_hdmi(struct hda_codec *codec)
{ struct hdmi_spec *spec;
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.
*/
codec->display_power_control = 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.