/* no pin nid is associated with the kctl now * tbd: associate pin nid to eld ctl later
*/
err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err;
if (spec->dyn_pin_out) /* Disable pin out until stream is active */
pin_out = 0; else /* Enable pin out: some machines with GM965 gets broken output * when the pin is disabled or changed while using with HDMI
*/
pin_out = PIN_OUT;
for (i = 0; i < 8; i++) {
size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i);
codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
} #endif
}
staticvoid hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
{ #ifdef BE_PARANOID int i, j; int size; int pi, bi; for (i = 0; i < 8; i++) {
size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i); if (size == 0) continue;
hdmi_set_dip_index(codec, pin_nid, i, 0x0); for (j = 1; j < 1000; j++) {
hdmi_write_dip_byte(codec, pin_nid, 0x0);
hdmi_get_dip_index(codec, pin_nid, &pi, &bi); if (pi != i)
codec_dbg(codec, "dip index %d: %d != %d\n",
bi, pi, i); if (bi == 0) /* byte index wrapped around */ break;
}
codec_dbg(codec, "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
i, size, j);
} #endif
}
staticvoid hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
{
u8 *bytes = (u8 *)hdmi_ai;
u8 sum = 0; int i;
hdmi_ai->checksum = 0;
for (i = 0; i < sizeof(*hdmi_ai); i++)
sum += bytes[i];
hdmi_ai->checksum = -sum;
}
staticvoid hdmi_fill_audio_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid,
u8 *dip, int size)
{ int i;
hdmi_debug_dip_size(codec, pin_nid);
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < size; i++)
hdmi_write_dip_byte(codec, pin_nid, dip[i]);
}
staticbool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
u8 *dip, int size)
{
u8 val; int i;
staticvoid hdmi_pin_setup_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id, int ca, int active_channels, int conn_type)
{ struct hdmi_spec *spec = codec->spec; union audio_infoframe ai;
/* * sizeof(ai) is used instead of sizeof(*hdmi_ai) or * sizeof(*dp_ai) to avoid partial match/update problems when * the user switches between HDMI/DP monitors.
*/ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, sizeof(ai))) {
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));
hdmi_start_infoframe_trans(codec, pin_nid);
}
}
void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm)
{ struct hdmi_spec *spec = codec->spec; struct hdac_chmap *chmap = &spec->chmap;
hda_nid_t pin_nid = per_pin->pin_nid; int dev_id = per_pin->dev_id; int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; int ca;
if (!channels) return;
snd_hda_set_dev_select(codec, pin_nid, dev_id);
/* 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);
eld = &per_pin->sink_eld;
ca = snd_hdac_channel_allocation(&codec->core,
eld->info.spk_alloc, channels,
per_pin->chmap_set, non_pcm, per_pin->chmap);
/* * always configure channel mapping, it may have been changed by the * user in the meantime
*/
snd_hdac_setup_channel_mapping(&spec->chmap,
pin_nid, non_pcm, ca, channels,
per_pin->chmap, per_pin->chmap_set);
spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
ca, active_channels, eld->info.conn_type);
if (err) {
codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n"); return err;
}
if (spec->intel_hsw_fixup) {
/* * on recent platforms IEC Coding Type is required for HBR * support, read current Digital Converter settings and set * ICT bitfield if needed.
*/
param = snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
param = (param >> 16) & ~(AC_DIG3_ICT);
/* on recent platforms ICT mode is required for HBR support */ if (is_hbr_format(format))
param |= 0x1;
/* Try to find an available converter * If pin_idx is less then zero, just try to find an available converter. * Otherwise, try to find an available converter and get the cvt mux index * of the pin.
*/ staticint hdmi_choose_cvt(struct hda_codec *codec, int pin_idx, int *cvt_id, bool silent)
{ struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; struct hdmi_spec_per_cvt *per_cvt = NULL; int cvt_idx, mux_idx = 0;
/* pin_idx < 0 means no pin will be bound to the converter */ if (pin_idx < 0)
per_pin = NULL; 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);
per_cvt = get_cvt(spec, cvt_idx); if (per_cvt->assigned && !silent) return -EBUSY; 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);
/* Must not already be assigned */ if (per_cvt->assigned || per_cvt->silent_stream) continue; if (per_pin == NULL) break; /* Must be in pin's mux's list of converters */ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) break; /* Not in mux list */ if (mux_idx == per_pin->num_mux_nids) continue; break;
}
/* No free converters */ if (cvt_idx == spec->num_cvts) return -EBUSY;
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 */ staticint hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream)
{ struct hdmi_spec *spec = codec->spec; struct snd_pcm_runtime *runtime = substream->runtime; int cvt_idx, pcm_idx; struct hdmi_spec_per_cvt *per_cvt = NULL; int err;
pcm_idx = hinfo_to_pcm_index(codec, hinfo); if (pcm_idx < 0) return -EINVAL;
err = hdmi_choose_cvt(codec, -1, &cvt_idx, false); if (err) return err;
mutex_lock(&spec->pcm_lock);
pin_idx = hinfo_to_pin_index(codec, hinfo); /* no pin is assigned to the PCM * PA need pcm open successfully when probe
*/ if (pin_idx < 0) {
err = hdmi_pcm_open_no_pin(hinfo, codec, substream); goto unlock;
}
/* flip stripe flag for the assigned stream if supported */ if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE)
azx_stream(get_azx_dev(substream))->stripe = 1;
/* * HDA/HDMI auto parsing
*/ staticint hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{ struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid; int dev_id = per_pin->dev_id; int conns;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
codec_warn(codec, "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n",
pin_nid, get_wcaps(codec, pin_nid)); return -EINVAL;
}
/* all the device entries on the same pin have the same conn list */
per_pin->num_mux_nids = conns;
return 0;
}
staticint hdmi_find_pcm_slot(struct hdmi_spec *spec, struct hdmi_spec_per_pin *per_pin)
{ int i;
for (i = 0; i < spec->pcm_used; i++) { if (!test_bit(i, &spec->pcm_bitmap)) return i;
} return -EBUSY;
}
staticvoid hdmi_attach_hda_pcm(struct hdmi_spec *spec, struct hdmi_spec_per_pin *per_pin)
{ int idx;
/* pcm already be attached to the pin */ if (per_pin->pcm) return; /* try the previously used slot at first */
idx = per_pin->prev_pcm_idx; if (idx >= 0) { if (!test_bit(idx, &spec->pcm_bitmap)) goto found;
per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */
}
idx = hdmi_find_pcm_slot(spec, per_pin); if (idx == -EBUSY) return;
found:
per_pin->pcm_idx = idx;
per_pin->pcm = get_hdmi_pcm(spec, idx);
set_bit(idx, &spec->pcm_bitmap);
}
staticvoid hdmi_detach_hda_pcm(struct hdmi_spec *spec, struct hdmi_spec_per_pin *per_pin)
{ int idx;
/* pcm already be detached from the pin */ if (!per_pin->pcm) return;
idx = per_pin->pcm_idx;
per_pin->pcm_idx = -1;
per_pin->prev_pcm_idx = idx; /* remember the previous index */
per_pin->pcm = NULL; if (idx >= 0 && idx < spec->pcm_used)
clear_bit(idx, &spec->pcm_bitmap);
}
/* for monitor disconnection, save pcm_idx firstly */
pcm_idx = per_pin->pcm_idx;
/* * pcm_idx >=0 before update_eld() means it is in monitor * disconnected event. Jack must be fetched before update_eld().
*/
pcm_jack = pin_idx_to_pcm_jack(codec, 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.
*/ if (pcm_idx == -1)
pcm_idx = per_pin->pcm_idx; if (!pcm_jack)
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
if (eld->eld_valid)
snd_show_eld(hda_codec_dev(codec), &eld->info);
/* * Re-setup pin and infoframe. This is needed e.g. when * - sink is first plugged-in * - transcoder can change during stream playback on Haswell * and this can make HW reset converter selection on a pin.
*/ if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
pin_cvt_fixup(codec, per_pin, 0);
snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
}
/* update ELD and jack state via HD-audio verbs */ staticvoid hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, int repoll)
{ struct hda_codec *codec = per_pin->codec; struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; struct device *dev = hda_codec_dev(codec);
hda_nid_t pin_nid = per_pin->pin_nid; int dev_id = per_pin->dev_id; /* * Always execute a GetPinSense verb here, even when called from * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited * response's PD bit is not the real PD value, but indicates that * the real PD value changed. An older version of the HD-audio * specification worked this way. Hence, we just ignore the data in * the unsolicited response to avoid custom WARs.
*/ int present; int ret;
#ifdef CONFIG_PM if (dev->power.runtime_status == RPM_SUSPENDING) return; #endif
ret = snd_hda_power_up_pm(codec); if (ret < 0 && pm_runtime_suspended(dev)) goto out;
/* * For DP MST audio, Configuration Default is the same for * all device entries on the same pin
*/
config = snd_hda_codec_get_pincfg(codec, pin_nid); if (get_defcfg_connect(config) == AC_JACK_PORT_NONE &&
!spec->force_connect) return 0;
/* * To simplify the implementation, malloc all * the virtual pins in the initialization statically
*/ if (spec->intel_hsw_fixup) { /* * On Intel platforms, device entries count returned * by AC_PAR_DEVLIST_LEN is dynamic, and depends on * the type of receiver that is connected. Allocate pin * structures based on worst case.
*/
dev_num = spec->dev_num;
} elseif (codec->dp_mst) {
dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1; /* * spec->dev_num is the maxinum number of device entries * among all the pins
*/
spec->dev_num = (spec->dev_num > dev_num) ?
spec->dev_num : dev_num;
} else { /* * If the platform doesn't support DP MST, * manually set dev_num to 1. This means * the pin has only one device entry.
*/
dev_num = 1;
spec->dev_num = 1;
}
for (i = 0; i < dev_num; i++) {
pin_idx = spec->num_pins;
per_pin = snd_array_new(&spec->pins);
if (!per_pin) return -ENOMEM;
per_pin->pcm = NULL;
per_pin->pcm_idx = -1;
per_pin->prev_pcm_idx = -1;
per_pin->pin_nid = pin_nid;
per_pin->pin_nid_idx = spec->num_nids;
per_pin->dev_id = i;
per_pin->non_pcm = false;
snd_hda_set_dev_select(codec, pin_nid, i);
err = hdmi_read_pin_conn(codec, pin_idx); if (err < 0) return err; if (!is_jack_detectable(codec, pin_nid))
codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid);
spec->num_pins++;
}
spec->num_nids++;
/* * hdmi_add_pin() assumes total amount of converters to * be known, so first discover all converters
*/ for (i = 0; i < nodes; i++) {
hda_nid_t nid = start_nid + i;
caps = get_wcaps(codec, nid);
if (!(caps & AC_WCAP_DIGITAL)) continue;
if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
hdmi_add_cvt(codec, nid);
}
/* discover audio pins */ for (i = 0; i < nodes; i++) {
hda_nid_t nid = start_nid + i;
caps = get_wcaps(codec, nid);
if (!(caps & AC_WCAP_DIGITAL)) continue;
if (get_wcaps_type(caps) == AC_WID_PIN)
hdmi_add_pin(codec, nid);
}
mutex_lock(&codec->spdif_mutex);
spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid); /* Add sanity check to pass klockwork check. * This should never happen.
*/ if (WARN_ON(spdif == NULL)) {
mutex_unlock(&codec->spdif_mutex); returntrue;
}
non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
mutex_unlock(&codec->spdif_mutex); return non_pcm;
}
/* * HDMI callbacks
*/
int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsignedint stream_tag, unsignedint format, struct snd_pcm_substream *substream)
{
hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; int pin_idx; struct hdmi_spec_per_pin *per_pin; struct snd_pcm_runtime *runtime = substream->runtime; bool non_pcm; int pinctl, stripe; int err = 0;
mutex_lock(&spec->pcm_lock);
pin_idx = hinfo_to_pin_index(codec, hinfo); if (pin_idx < 0) { /* when pcm is not bound to a pin skip pin setup and return 0 * to make audio playback be ongoing
*/
pin_cvt_fixup(codec, NULL, cvt_nid);
snd_hda_codec_setup_stream(codec, cvt_nid,
stream_tag, 0, format); goto unlock;
}
per_pin = get_pin(spec, pin_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->core, per_pin->pin_nid,
per_pin->dev_id, runtime->rate);
snd_hda_spdif_ctls_unassign(codec, pcm_idx);
clear_bit(pcm_idx, &spec->pcm_in_use);
pin_idx = hinfo_to_pin_index(codec, hinfo); /* * In such a case, return 0 to match the behavior in * hdmi_pcm_open()
*/ if (pin_idx < 0) goto unlock;
int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec)
{ struct hdmi_spec *spec = codec->spec; int idx, pcm_num;
/* 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);
int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec)
{ struct hdmi_spec *spec = codec->spec; int dev, err; int pin_idx, pcm_idx;
for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { if (!get_pcm_rec(spec, pcm_idx)->pcm) { /* no PCM: mark this for skipping permanently */
set_bit(pcm_idx, &spec->pcm_bitmap); continue;
}
err = generic_hdmi_build_jack(codec, pcm_idx); if (err < 0) return err;
/* create the spdif for each pcm * pin will be bound when monitor is connected
*/
err = snd_hda_create_dig_out_ctls(codec,
0, spec->cvt_nids[0],
HDA_PCM_TYPE_HDMI); if (err < 0) return err;
snd_hda_spdif_ctls_unassign(codec, pcm_idx);
dev = get_pcm_rec(spec, pcm_idx)->device; if (dev != SNDRV_PCM_INVALID_DEVICE) { /* add control for ELD Bytes */
err = hdmi_create_eld_ctl(codec, pcm_idx, dev); if (err < 0) return err;
}
}
/* check whether both HD-audio and DRM PCI devices belong to the same bus */ staticint match_bound_vga(struct device *dev, int subtype, void *data)
{ struct hdac_bus *bus = data; struct pci_dev *pci, *master;
if (!pin_nid) return; if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN) 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;
/* set up the private drm_audio_ops from the template */ void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec, conststruct drm_audio_component_audio_ops *ops)
{ struct hdmi_spec *spec = codec->spec;
spec->drm_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->drm_audio_ops.pin2port = ops->pin2port;
spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
spec->drm_audio_ops.master_bind = ops->master_bind;
spec->drm_audio_ops.master_unbind = ops->master_unbind;
}
EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_drm_audio_ops, "SND_HDA_CODEC_HDMI");
staticint generichdmi_probe(struct hda_codec *codec, conststruct hda_device_id *id)
{ int err;
err = snd_hda_hdmi_generic_probe(codec); if (err < 0) return err; /* * Glenfly GPUs have two codecs, stream switches from one codec to * another, need to do actual clean-ups in codec_cleanup_stream
*/ if (id->driver_data == MODEL_GF)
codec->no_sticky_stream = 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.