From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom
---
kernel/sound/pci/hda/patch_hdmi.c | 1197 +++++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 826 insertions(+), 371 deletions(-)
diff --git a/kernel/sound/pci/hda/patch_hdmi.c b/kernel/sound/pci/hda/patch_hdmi.c
index d21a4eb..b1c57c6 100644
--- a/kernel/sound/pci/hda/patch_hdmi.c
+++ b/kernel/sound/pci/hda/patch_hdmi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
@@ -13,24 +14,11 @@
*
* Maintained by:
* Wu Fengguang <wfg@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -41,29 +29,23 @@
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
#include <sound/hda_chmap.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_jack.h"
+#include "hda_controller.h"
static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
-#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807)
-#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808)
-#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809)
-#define is_broxton(codec) ((codec)->core.vendor_id == 0x8086280a)
-#define is_kabylake(codec) ((codec)->core.vendor_id == 0x8086280b)
-#define is_geminilake(codec) (((codec)->core.vendor_id == 0x8086280d) || \
- ((codec)->core.vendor_id == 0x80862800))
-#define is_cannonlake(codec) ((codec)->core.vendor_id == 0x8086280c)
-#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
- || is_skylake(codec) || is_broxton(codec) \
- || is_kabylake(codec)) || is_geminilake(codec) \
- || is_cannonlake(codec)
-#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882)
-#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883)
-#define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec))
+static bool enable_acomp = true;
+module_param(enable_acomp, bool, 0444);
+MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
+
+static bool enable_silent_stream =
+IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM);
+module_param(enable_silent_stream, bool, 0644);
+MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices");
struct hdmi_spec_per_cvt {
hda_nid_t cvt_nid;
@@ -96,6 +78,7 @@
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? */
@@ -108,16 +91,19 @@
/* operations used by generic code that can be overridden by patches */
struct hdmi_ops {
int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
- unsigned char *buf, int *eld_size);
+ int dev_id, unsigned char *buf, int *eld_size);
void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id,
int ca, int active_channels, int conn_type);
/* enable/disable HBR (HD passthrough) */
- int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
+ int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr);
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
- hda_nid_t pin_nid, u32 stream_tag, int format);
+ hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+ int format);
void (*pin_cvt_fixup)(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
@@ -131,6 +117,7 @@
};
struct hdmi_spec {
+ struct hda_codec *codec;
int num_cvts;
struct snd_array cvts; /* struct hdmi_spec_per_cvt */
hda_nid_t cvt_nids[4]; /* only for haswell fix */
@@ -155,6 +142,7 @@
struct snd_array pins; /* struct hdmi_spec_per_pin */
struct hdmi_pcm pcm_rec[16];
struct mutex pcm_lock;
+ struct mutex bind_lock; /* for audio component binding */
/* pcm_bitmap means which pcms have been assigned to pins*/
unsigned long pcm_bitmap;
int pcm_used; /* counter of pcm_rec[] */
@@ -169,18 +157,27 @@
bool dyn_pin_out;
bool dyn_pcm_assign;
+ bool dyn_pcm_no_legacy;
+ bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */
+
+ bool intel_hsw_fixup; /* apply Intel platform-specific fixups */
/*
* Non-generic VIA/NVIDIA specific
*/
struct hda_multi_out multiout;
struct hda_pcm_stream pcm_playback;
- /* i915/powerwell (Haswell+/Valleyview+) specific */
- bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
+ bool use_acomp_notifier; /* use eld_notify callback for hotplug */
+ bool acomp_registered; /* audio component registered in this driver */
+ bool force_connect; /* force connectivity */
struct drm_audio_component_audio_ops drm_audio_ops;
+ int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
struct hdac_chmap chmap;
hda_nid_t vendor_nid;
+ const int *port_map;
+ int port_num;
+ bool send_silent_stream; /* Flag to enable silent stream feature */
};
#ifdef CONFIG_SND_HDA_COMPONENT
@@ -273,7 +270,7 @@
if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
return pcm_idx;
- codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo);
return -EINVAL;
}
@@ -291,7 +288,8 @@
return pin_idx;
}
- codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo,
+ hinfo_to_pcm_index(codec, hinfo));
return -EINVAL;
}
@@ -389,7 +387,8 @@
}
static const struct snd_kcontrol_new eld_bytes_ctl = {
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
@@ -657,20 +656,37 @@
return true;
}
+static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
+{
+ snd_hda_set_dev_select(codec, nid, dev_id);
+
+ return snd_hdmi_get_eld(codec, nid, buf, eld_size);
+}
+
static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
- hda_nid_t pin_nid,
+ 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;
memset(&ai, 0, sizeof(ai));
- if (conn_type == 0) { /* HDMI */
+ if ((conn_type == 0) || /* HDMI */
+ /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */
+ (conn_type == 1 && spec->nv_dp_workaround)) {
struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
- hdmi_ai->type = 0x84;
- hdmi_ai->ver = 0x01;
- hdmi_ai->len = 0x0a;
+ if (conn_type == 0) { /* HDMI */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ } else {/* Nvidia DP */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x1b;
+ hdmi_ai->len = 0x11 << 2;
+ }
hdmi_ai->CC02_CT47 = active_channels - 1;
hdmi_ai->CA = ca;
hdmi_checksum_audio_infoframe(hdmi_ai);
@@ -687,6 +703,8 @@
pin_nid);
return;
}
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
/*
* sizeof(ai) is used instead of sizeof(*hdmi_ai) or
@@ -713,6 +731,7 @@
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;
@@ -720,6 +739,8 @@
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)
@@ -746,8 +767,8 @@
pin_nid, non_pcm, ca, channels,
per_pin->chmap, per_pin->chmap_set);
- spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
- eld->info.conn_type);
+ spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
+ ca, active_channels, eld->info.conn_type);
per_pin->non_pcm = non_pcm;
}
@@ -756,7 +777,7 @@
* Unsolicited events
*/
-static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
int dev_id)
@@ -767,42 +788,31 @@
if (pin_idx < 0)
return;
mutex_lock(&spec->pcm_lock);
- if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
- snd_hda_jack_report_sync(codec);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
mutex_unlock(&spec->pcm_lock);
}
static void jack_callback(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
+ /* stop polling when notification is enabled */
+ if (codec_has_acomp(codec))
+ return;
+
+ check_presence_and_report(codec, jack->nid, jack->dev_id);
}
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res,
+ struct hda_jack_tbl *jack)
{
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- struct hda_jack_tbl *jack;
- int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
-
- /*
- * assume DP MST uses dyn_pcm_assign and acomp and
- * never comes here
- * if DP MST supports unsol event, below code need
- * consider dev_entry
- */
- jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
- if (!jack)
- return;
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",
- codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
+ codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA),
!!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
+ check_presence_and_report(codec, jack->nid, jack->dev_id);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -821,10 +831,12 @@
cp_ready);
/* TODO */
- if (cp_state)
+ if (cp_state) {
;
- if (cp_ready)
+ }
+ if (cp_ready) {
;
+ }
}
@@ -832,14 +844,27 @@
{
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ struct hda_jack_tbl *jack;
- if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
+ if (codec_has_acomp(codec))
+ return;
+
+ if (codec->dp_mst) {
+ int dev_entry =
+ (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+ } else {
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+ }
+
+ if (!jack) {
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
if (subtag == 0)
- hdmi_intrinsic_event(codec, res);
+ hdmi_intrinsic_event(codec, res, jack);
else
hdmi_non_intrinsic_event(codec, res);
}
@@ -874,11 +899,12 @@
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
- bool hbr)
+ int dev_id, bool hbr)
{
int pinctl, new_pinctl;
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -908,20 +934,22 @@
}
static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
- hda_nid_t pin_nid, u32 stream_tag, int format)
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
{
struct hdmi_spec *spec = codec->spec;
unsigned int param;
int err;
- err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
+ err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id,
+ is_hbr_format(format));
if (err) {
codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
return err;
}
- if (is_haswell_plus(codec)) {
+ if (spec->intel_hsw_fixup) {
/*
* on recent platforms IEC Coding Type is required for HBR
@@ -963,6 +991,13 @@
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);
+ 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++) {
@@ -1234,7 +1269,12 @@
set_bit(pcm_idx, &spec->pcm_in_use);
per_pin = get_pin(spec, pin_idx);
per_pin->cvt_nid = per_cvt->cvt_nid;
+ per_pin->silent_stream = false;
hinfo->nid = per_cvt->cvt_nid;
+
+ /* 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;
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,
@@ -1288,6 +1328,8 @@
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,
@@ -1296,31 +1338,67 @@
return -EINVAL;
}
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ if (spec->intel_hsw_fixup) {
+ conns = spec->num_cvts;
+ memcpy(per_pin->mux_nids, spec->cvt_nids,
+ sizeof(hda_nid_t) * conns);
+ } else {
+ conns = snd_hda_get_raw_connections(codec, pin_nid,
+ per_pin->mux_nids,
+ HDA_MAX_CONNECTIONS);
+ }
+
/* all the device entries on the same pin have the same conn list */
- per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
- per_pin->mux_nids,
- HDA_MAX_CONNECTIONS);
+ per_pin->num_mux_nids = conns;
return 0;
}
static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
- struct hdmi_spec_per_pin *per_pin)
+ struct hdmi_spec_per_pin *per_pin)
{
int i;
- /* try the prefer PCM */
- if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
- return per_pin->pin_nid_idx;
+ /* on the new machines, try to assign the pcm slot dynamically,
+ * not use the preferred fixed map (legacy way) anymore.
+ */
+ if (spec->dyn_pcm_no_legacy)
+ goto last_try;
- /* have a second try; check the "reserved area" over num_pins */
+ /*
+ * generic_hdmi_build_pcms() may allocate extra PCMs on some
+ * platforms (with maximum of 'num_nids + dev_num - 1')
+ *
+ * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n
+ * if m==0. This guarantees that dynamic pcm assignments are compatible
+ * with the legacy static per_pin-pcm assignment that existed in the
+ * days before DP-MST.
+ *
+ * Intel DP-MST prefers this legacy behavior for compatibility, too.
+ *
+ * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
+ */
+
+ if (per_pin->dev_id == 0 || spec->intel_hsw_fixup) {
+ if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+ return per_pin->pin_nid_idx;
+ } else {
+ i = spec->num_nids + (per_pin->dev_id - 1);
+ if (i < spec->pcm_used && !(test_bit(i, &spec->pcm_bitmap)))
+ return i;
+ }
+
+ /* have a second try; check the area over num_nids */
for (i = spec->num_nids; i < spec->pcm_used; i++) {
if (!test_bit(i, &spec->pcm_bitmap))
return i;
}
+ last_try:
/* the last try; check the empty slots in pins */
- for (i = 0; i < spec->num_nids; i++) {
+ for (i = 0; i < spec->pcm_used; i++) {
if (!test_bit(i, &spec->pcm_bitmap))
return i;
}
@@ -1428,21 +1506,60 @@
per_pin->channels = 0;
}
+static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (per_pin->pcm_idx >= 0)
+ return spec->pcm_rec[per_pin->pcm_idx].jack;
+ else
+ return NULL;
+}
+
/* update per_pin ELD from the given new ELD;
* setup info frame and notification accordingly
+ * also notify ELD kctl and report jack status changes
*/
static void update_eld(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
- struct hdmi_eld *eld)
+ struct hdmi_eld *eld,
+ int repoll)
{
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *pcm_jack;
bool old_eld_valid = pin_eld->eld_valid;
bool eld_changed;
- int pcm_idx = -1;
+ int pcm_idx;
+
+ if (eld->eld_valid) {
+ if (eld->eld_size <= 0 ||
+ snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
+ eld->eld_size) < 0) {
+ eld->eld_valid = false;
+ if (repoll) {
+ schedule_delayed_work(&per_pin->work,
+ msecs_to_jiffies(300));
+ return;
+ }
+ }
+ }
+
+ if (!eld->eld_valid || eld->eld_size <= 0) {
+ eld->eld_valid = false;
+ eld->eld_size = 0;
+ }
/* 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->dyn_pcm_assign) {
if (eld->eld_valid) {
hdmi_attach_hda_pcm(spec, per_pin);
@@ -1457,23 +1574,29 @@
*/
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_hdmi_show_eld(codec, &eld->info);
eld_changed = (pin_eld->eld_valid != eld->eld_valid);
- if (eld->eld_valid && pin_eld->eld_valid)
+ eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
+ if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
if (pin_eld->eld_size != eld->eld_size ||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
eld->eld_size) != 0)
eld_changed = true;
- pin_eld->monitor_present = eld->monitor_present;
- pin_eld->eld_valid = eld->eld_valid;
- pin_eld->eld_size = eld->eld_size;
- if (eld->eld_valid)
- memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size);
- pin_eld->info = eld->info;
+ if (eld_changed) {
+ pin_eld->monitor_present = eld->monitor_present;
+ pin_eld->eld_valid = eld->eld_valid;
+ pin_eld->eld_size = eld->eld_size;
+ if (eld->eld_valid)
+ memcpy(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size);
+ pin_eld->info = eld->info;
+ }
/*
* Re-setup pin and infoframe. This is needed e.g. when
@@ -1491,17 +1614,23 @@
SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
+
+ if (eld_changed && pcm_jack)
+ snd_jack_report(pcm_jack,
+ (eld->monitor_present && eld->eld_valid) ?
+ SND_JACK_AVOUT : 0);
}
/* update ELD and jack state via HD-audio verbs */
-static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
+static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
int repoll)
{
- struct hda_jack_tbl *jack;
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
@@ -1511,10 +1640,18 @@
* the unsolicited response to avoid custom WARs.
*/
int present;
- bool ret;
- bool do_repoll = false;
+ int ret;
- present = snd_hda_pin_sense(codec, pin_nid);
+#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;
+
+ present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
mutex_lock(&per_pin->lock);
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
@@ -1528,61 +1665,104 @@
codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
if (eld->eld_valid) {
- if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
- &eld->eld_size) < 0)
+ if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
+ eld->eld_buffer, &eld->eld_size) < 0)
eld->eld_valid = false;
- else {
- if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
- eld->eld_size) < 0)
- eld->eld_valid = false;
- }
- if (!eld->eld_valid && repoll)
- do_repoll = true;
}
- if (do_repoll)
- schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
- else
- update_eld(codec, per_pin, eld);
-
- ret = !repoll || !eld->monitor_present || eld->eld_valid;
-
- jack = snd_hda_jack_tbl_get(codec, pin_nid);
- if (jack) {
- jack->block_report = !ret;
- jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
- AC_PINSENSE_PRESENCE : 0;
- }
+ update_eld(codec, per_pin, eld, repoll);
mutex_unlock(&per_pin->lock);
- return ret;
+ out:
+ snd_hda_power_down_pm(codec);
}
-static struct snd_jack *pin_idx_to_jack(struct hda_codec *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 *spec = codec->spec;
- struct snd_jack *jack = NULL;
- struct hda_jack_tbl *jack_tbl;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int cvt_idx, pin_idx, err;
+ unsigned int format;
- /* if !dyn_pcm_assign, get jack from hda_jack_tbl
- * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
- * NULL even after snd_hda_jack_tbl_clear() is called to
- * free snd_jack. This may cause access invalid memory
- * when calling snd_jack_report
- */
- if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
- jack = spec->pcm_rec[per_pin->pcm_idx].jack;
- else if (!spec->dyn_pcm_assign) {
- /*
- * jack tbl doesn't support DP MST
- * DP MST will use dyn_pcm_assign,
- * so DP MST will never come here
- */
- jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
- if (jack_tbl)
- jack = jack_tbl->jack;
+ mutex_lock(&per_pin->lock);
+
+ if (per_pin->setup) {
+ codec_dbg(codec, "hdmi: PCM already open, no silent stream\n");
+ goto unlock_out;
}
- return jack;
+
+ 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(&per_pin->lock);
}
/* update ELD and jack state via audio component */
@@ -1591,69 +1771,53 @@
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld;
- struct snd_jack *jack = NULL;
- int size;
+ bool monitor_prev, monitor_next;
mutex_lock(&per_pin->lock);
eld->monitor_present = false;
- size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
+ monitor_prev = per_pin->sink_eld.monitor_present;
+ eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
per_pin->dev_id, &eld->monitor_present,
eld->eld_buffer, ELD_MAX_SIZE);
- if (size > 0) {
- size = min(size, ELD_MAX_SIZE);
- if (snd_hdmi_parse_eld(codec, &eld->info,
- eld->eld_buffer, size) < 0)
- size = -EINVAL;
- }
-
- if (size > 0) {
- eld->eld_valid = true;
- eld->eld_size = size;
- } else {
- eld->eld_valid = false;
- eld->eld_size = 0;
- }
-
- /* pcm_idx >=0 before update_eld() means it is in monitor
- * disconnected event. Jack must be fetched before update_eld()
- */
- jack = pin_idx_to_jack(codec, per_pin);
- update_eld(codec, per_pin, eld);
- if (jack == NULL)
- jack = pin_idx_to_jack(codec, per_pin);
- if (jack == NULL)
- goto unlock;
- snd_jack_report(jack,
- eld->monitor_present ? SND_JACK_AVOUT : 0);
- unlock:
+ eld->eld_valid = (eld->eld_size > 0);
+ update_eld(codec, per_pin, eld, 0);
+ monitor_next = per_pin->sink_eld.monitor_present;
mutex_unlock(&per_pin->lock);
-}
-static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
-{
- struct hda_codec *codec = per_pin->codec;
- int ret;
+ /*
+ * Power-up will call hdmi_present_sense, so the PM calls
+ * have to be done without mutex held.
+ */
- /* no temporary power up/down needed for component notifier */
- if (!codec_has_acomp(codec)) {
- ret = snd_hda_power_up_pm(codec);
- if (ret < 0 && pm_runtime_suspended(hda_codec_dev(codec))) {
- snd_hda_power_down_pm(codec);
- return false;
+ if (spec->send_silent_stream) {
+ int pm_ret;
+
+ if (!monitor_prev && monitor_next) {
+ pm_ret = snd_hda_power_up_pm(codec);
+ if (pm_ret < 0)
+ codec_err(codec,
+ "Monitor plugged-in, Failed to power up codec ret=[%d]\n",
+ 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,
+ "Monitor plugged-out, Failed to power down codec ret=[%d]\n",
+ pm_ret);
}
}
+}
- if (codec_has_acomp(codec)) {
- sync_eld_via_acomp(codec, per_pin);
- ret = false; /* don't call snd_hda_jack_report_sync() */
- } else {
- ret = hdmi_present_sense_via_verbs(per_pin, repoll);
- }
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+{
+ struct hda_codec *codec = per_pin->codec;
if (!codec_has_acomp(codec))
- snd_hda_power_down_pm(codec);
-
- return ret;
+ hdmi_present_sense_via_verbs(per_pin, repoll);
+ else
+ sync_eld_via_acomp(codec, per_pin);
}
static void hdmi_repoll_eld(struct work_struct *work)
@@ -1664,7 +1828,8 @@
struct hdmi_spec *spec = codec->spec;
struct hda_jack_tbl *jack;
- jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+ jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
+ per_pin->dev_id);
if (jack)
jack->jack_dirty = 1;
@@ -1672,13 +1837,9 @@
per_pin->repoll_count = 0;
mutex_lock(&spec->pcm_lock);
- if (hdmi_present_sense(per_pin, per_pin->repoll_count))
- snd_hda_jack_report_sync(per_pin->codec);
+ hdmi_present_sense(per_pin, per_pin->repoll_count);
mutex_unlock(&spec->pcm_lock);
}
-
-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)
{
@@ -1698,14 +1859,15 @@
* 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)
+ 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 (is_haswell_plus(codec)) {
+ if (spec->intel_hsw_fixup) {
/*
* On Intel platforms, device entries number is
* changed dynamically. If there is a DP MST
@@ -1754,8 +1916,6 @@
per_pin->dev_id = i;
per_pin->non_pcm = false;
snd_hda_set_dev_select(codec, pin_nid, i);
- if (is_haswell_plus(codec))
- intel_haswell_fixup_connect_list(codec, pin_nid);
err = hdmi_read_pin_conn(codec, pin_idx);
if (err < 0)
return err;
@@ -1802,35 +1962,59 @@
return 0;
}
+static const struct snd_pci_quirk force_connect_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
+ {}
+};
+
static int hdmi_parse_codec(struct hda_codec *codec)
{
- hda_nid_t nid;
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t start_nid;
+ unsigned int caps;
int i, nodes;
+ const struct snd_pci_quirk *q;
- nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid);
- if (!nid || nodes < 0) {
+ nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid);
+ if (!start_nid || nodes < 0) {
codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
- for (i = 0; i < nodes; i++, nid++) {
- unsigned int caps;
- unsigned int type;
+ q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list);
+
+ if (q && q->value)
+ spec->force_connect = true;
+
+ /*
+ * 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);
- type = get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
- switch (type) {
- case AC_WID_AUD_OUT:
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
hdmi_add_cvt(codec, nid);
- break;
- case AC_WID_PIN:
+ }
+
+ /* 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);
- break;
- }
}
return 0;
@@ -1871,10 +2055,9 @@
struct hdmi_spec *spec = codec->spec;
int pin_idx;
struct hdmi_spec_per_pin *per_pin;
- hda_nid_t pin_nid;
struct snd_pcm_runtime *runtime = substream->runtime;
bool non_pcm;
- int pinctl;
+ int pinctl, stripe;
int err = 0;
mutex_lock(&spec->pcm_lock);
@@ -1895,7 +2078,6 @@
goto unlock;
}
per_pin = get_pin(spec, pin_idx);
- pin_nid = per_pin->pin_nid;
/* Verify pin:cvt selections to avoid silent audio after S3.
* After S3, the audio driver restores pin:cvt selections
@@ -1910,27 +2092,37 @@
/* 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, pin_nid, per_pin->dev_id,
- runtime->rate);
+ snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
per_pin->channels = substream->runtime->channels;
per_pin->setup = true;
+ if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) {
+ stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core,
+ substream);
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_STRIPE_CONTROL,
+ stripe);
+ }
+
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
mutex_unlock(&per_pin->lock);
if (spec->dyn_pin_out) {
- pinctl = snd_hda_codec_read(codec, pin_nid, 0,
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write(codec, pin_nid, 0,
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pinctl | PIN_OUT);
}
/* snd_hda_set_dev_select() has been called before */
- err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
- stream_tag, format);
+ err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
+ per_pin->dev_id, stream_tag, format);
unlock:
mutex_unlock(&spec->pcm_lock);
return err;
@@ -1968,9 +2160,10 @@
goto unlock;
}
per_cvt = get_cvt(spec, cvt_idx);
- snd_BUG_ON(!per_cvt->assigned);
per_cvt->assigned = 0;
hinfo->nid = 0;
+
+ azx_stream(get_azx_dev(substream))->stripe = 0;
snd_hda_spdif_ctls_unassign(codec, pcm_idx);
clear_bit(pcm_idx, &spec->pcm_in_use);
@@ -1985,6 +2178,8 @@
per_pin = get_pin(spec, pin_idx);
if (spec->dyn_pin_out) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
@@ -2016,7 +2211,7 @@
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 hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2029,7 +2224,7 @@
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2043,7 +2238,7 @@
static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap, int prepared)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2059,7 +2254,7 @@
static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2069,15 +2264,26 @@
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int idx;
+ int idx, pcm_num;
/*
* for non-mst mode, pcm number is the same as before
- * for DP MST mode, pcm number is (nid number + dev_num - 1)
- * dev_num is the device entry number in a pin
- *
+ * for DP MST mode without extra PCM, pcm number is same
+ * for DP MST mode with extra PCMs, pcm number is
+ * (nid number + dev_num - 1)
+ * dev_num is the device entry number in a pin
*/
- for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
+
+ if (spec->dyn_pcm_no_legacy && codec->mst_no_extra_pcms)
+ pcm_num = spec->num_cvts;
+ else if (codec->mst_no_extra_pcms)
+ pcm_num = spec->num_nids;
+ else
+ pcm_num = spec->num_nids + spec->dev_num - 1;
+
+ codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
+
+ for (idx = 0; idx < pcm_num; idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
@@ -2109,15 +2315,23 @@
pcm->jack = NULL;
}
-static int add_hdmi_jack_kctl(struct hda_codec *codec,
- struct hdmi_spec *spec,
- int pcm_idx,
- const char *name)
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pcm_idx);
struct snd_jack *jack;
+ int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
int err;
- err = snd_jack_new(codec->card, name, SND_JACK_AVOUT, &jack,
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+ if (!spec->dyn_pcm_assign &&
+ !is_jack_detectable(codec, per_pin->pin_nid))
+ strncat(hdmi_str, " Phantom",
+ sizeof(hdmi_str) - strlen(hdmi_str) - 1);
+
+ err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack,
true, false);
if (err < 0)
return err;
@@ -2125,46 +2339,6 @@
spec->pcm_rec[pcm_idx].jack = jack;
jack->private_data = &spec->pcm_rec[pcm_idx];
jack->private_free = free_hdmi_jack_priv;
- return 0;
-}
-
-static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
-{
- char hdmi_str[32] = "HDMI/DP";
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin;
- struct hda_jack_tbl *jack;
- int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
- bool phantom_jack;
- int ret;
-
- if (pcmdev > 0)
- sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
-
- if (spec->dyn_pcm_assign)
- return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
-
- /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
- /* if !dyn_pcm_assign, it must be non-MST mode.
- * This means pcms and pins are statically mapped.
- * And pcm_idx is pin_idx.
- */
- per_pin = get_pin(spec, pcm_idx);
- phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
- if (phantom_jack)
- strncat(hdmi_str, " Phantom",
- sizeof(hdmi_str) - strlen(hdmi_str) - 1);
- ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
- phantom_jack);
- if (ret < 0)
- return ret;
- jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
- if (jack == NULL)
- return 0;
- /* assign jack->jack to pcm_rec[].jack to
- * align with dyn_pcm_assign mode
- */
- spec->pcm_rec[pcm_idx].jack = jack->jack;
return 0;
}
@@ -2257,6 +2431,7 @@
struct hdmi_spec *spec = codec->spec;
int pin_idx;
+ mutex_lock(&spec->bind_lock);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
@@ -2264,11 +2439,12 @@
snd_hda_set_dev_select(codec, pin_nid, dev_id);
hdmi_init_pin(codec, pin_nid);
- if (!codec_has_acomp(codec))
- snd_hda_jack_detect_enable_callback(codec, pin_nid,
- codec->jackpoll_interval > 0 ?
- jack_callback : NULL);
+ if (codec_has_acomp(codec))
+ continue;
+ snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id,
+ jack_callback);
}
+ mutex_unlock(&spec->bind_lock);
return 0;
}
@@ -2301,10 +2477,12 @@
struct hdmi_spec *spec = codec->spec;
int pin_idx, pcm_idx;
- if (codec_has_acomp(codec)) {
+ if (spec->acomp_registered) {
+ snd_hdac_acomp_exit(&codec->bus->core);
+ } else if (codec_has_acomp(codec)) {
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
- codec->relaxed_resume = 0;
}
+ codec->relaxed_resume = 0;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@@ -2344,7 +2522,7 @@
int pin_idx;
codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@@ -2367,7 +2545,7 @@
};
static const struct hdmi_ops generic_standard_hdmi_ops = {
- .pin_get_eld = snd_hdmi_get_eld,
+ .pin_get_eld = hdmi_pin_get_eld,
.pin_setup_infoframe = hdmi_pin_setup_infoframe,
.pin_hbr_setup = hdmi_pin_hbr_setup,
.setup_stream = hdmi_setup_stream,
@@ -2382,15 +2560,17 @@
if (!spec)
return -ENOMEM;
+ spec->codec = codec;
spec->ops = generic_standard_hdmi_ops;
spec->dev_num = 1; /* initialize to 1 */
mutex_init(&spec->pcm_lock);
+ mutex_init(&spec->bind_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,
+ spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc;
codec->spec = spec;
hdmi_array_init(spec, 4);
@@ -2420,32 +2600,140 @@
}
/*
+ * generic audio component binding
+ */
+
+/* turn on / off the unsol event jack detection dynamically */
+static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, bool use_acomp)
+{
+ struct hda_jack_tbl *tbl;
+
+ tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (tbl) {
+ /* clear unsol even if component notifier is used, or re-enable
+ * if notifier is cleared
+ */
+ unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, val);
+ }
+}
+
+/* set up / clear component notifier dynamically */
+static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
+ bool use_acomp)
+{
+ struct hdmi_spec *spec;
+ int i;
+
+ spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
+ mutex_lock(&spec->bind_lock);
+ spec->use_acomp_notifier = use_acomp;
+ spec->codec->relaxed_resume = use_acomp;
+ spec->codec->bus->keep_power = 0;
+ /* reprogram each jack detection logic depending on the notifier */
+ for (i = 0; i < spec->num_pins; i++)
+ reprogram_jack_detect(spec->codec,
+ get_pin(spec, i)->pin_nid,
+ get_pin(spec, i)->dev_id,
+ use_acomp);
+ mutex_unlock(&spec->bind_lock);
+}
+
+/* enable / disable the notifier via master bind / unbind */
+static int generic_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, true);
+ return 0;
+}
+
+static void generic_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, false);
+}
+
+/* check whether both HD-audio and DRM PCI devices belong to the same bus */
+static int match_bound_vga(struct device *dev, int subtype, void *data)
+{
+ struct hdac_bus *bus = data;
+ struct pci_dev *pci, *master;
+
+ if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
+ return 0;
+ master = to_pci_dev(bus->dev);
+ pci = to_pci_dev(dev);
+ return master->bus == pci->bus;
+}
+
+/* audio component notifier for AMD/Nvidia HDMI codecs */
+static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
+{
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid = spec->port2pin(codec, port);
+
+ 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;
+
+ check_presence_and_report(codec, pin_nid, dev_id);
+}
+
+/* set up the private drm_audio_ops from the template */
+static void setup_drm_audio_ops(struct hda_codec *codec,
+ const struct 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;
+}
+
+/* initialize the generic HDMI audio component */
+static void generic_acomp_init(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops,
+ int (*port2pin)(struct hda_codec *, int))
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!enable_acomp) {
+ codec_info(codec, "audio component disabled by module option\n");
+ return;
+ }
+
+ spec->port2pin = port2pin;
+ setup_drm_audio_ops(codec, ops);
+ if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
+ match_bound_vga, 0)) {
+ spec->acomp_registered = true;
+ }
+}
+
+/*
* Intel codec parsers and helpers
*/
-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 */
- codec_dbg(codec, "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_GLK_VENDOR_NID 0x0B
-#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 */
+#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,
bool update_tree)
@@ -2525,11 +2813,44 @@
static int intel_pin2port(void *audio_ptr, int pin_nid)
{
- int base_nid = intel_base_nid(audio_ptr);
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ int base_nid, i;
- if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3))
- return -1;
- return pin_nid - base_nid + 1; /* intel port is 1-based */
+ if (!spec->port_num) {
+ base_nid = intel_base_nid(codec);
+ if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3))
+ return -1;
+ return pin_nid - base_nid + 1;
+ }
+
+ /*
+ * 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 %d\n", pin_nid);
+ return -1;
+}
+
+static int intel_port2pin(struct hda_codec *codec, int port)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ 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];
}
static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
@@ -2538,24 +2859,23 @@
int pin_nid;
int dev_id = pipe;
- /* we assume only from port-B to port-D */
- if (port < 1 || port > 3)
+ pin_nid = intel_port2pin(codec, port);
+ if (!pin_nid)
return;
-
- pin_nid = port + intel_base_nid(codec) - 1; /* intel port is 1-based */
-
/* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume
*/
- if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
- return;
- /* ditto during suspend/resume process itself */
- if (snd_hdac_is_in_pm(&codec->core))
+ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND)
return;
snd_hdac_i915_set_bclk(&codec->bus->core);
check_presence_and_report(codec, pin_nid, dev_id);
}
+
+static const struct drm_audio_component_audio_ops intel_audio_ops = {
+ .pin2port = intel_pin2port,
+ .pin_eld_notify = intel_pin_eld_notify,
+};
/* register i915 component pin_eld_notify callback */
static void register_i915_notifier(struct hda_codec *codec)
@@ -2563,14 +2883,8 @@
struct hdmi_spec *spec = codec->spec;
spec->use_acomp_notifier = true;
- 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 = intel_pin2port;
- spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+ spec->port2pin = intel_port2pin;
+ 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 */
@@ -2579,10 +2893,12 @@
/* 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)
+ hda_nid_t pin_nid, int dev_id, 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);
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
}
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
@@ -2643,7 +2959,8 @@
}
/* Intel Haswell and onwards; audio component with eld notifier */
-static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid)
+static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
+ const int *port_map, int port_num)
{
struct hdmi_spec *spec;
int err;
@@ -2655,15 +2972,14 @@
codec->dp_mst = true;
spec->dyn_pcm_assign = true;
spec->vendor_nid = vendor_nid;
+ spec->port_map = port_map;
+ spec->port_num = port_num;
+ spec->intel_hsw_fixup = true;
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.
- */
- if (!is_haswell(codec) && !is_broadwell(codec))
- codec->core.link_power_control = 1;
+ codec->display_power_control = 1;
codec->patch_ops.set_power_state = haswell_set_power_state;
codec->depop_delay = 0;
@@ -2672,17 +2988,54 @@
spec->ops.setup_stream = i915_hsw_setup_stream;
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+ /*
+ * Enable silent stream feature, if it is enabled via
+ * module param or Kconfig option
+ */
+ if (enable_silent_stream)
+ spec->send_silent_stream = true;
+
return parse_intel_hdmi(codec);
}
static int patch_i915_hsw_hdmi(struct hda_codec *codec)
{
- return intel_hsw_common_init(codec, INTEL_VENDOR_NID);
+ return intel_hsw_common_init(codec, 0x08, NULL, 0);
}
static int patch_i915_glk_hdmi(struct hda_codec *codec)
{
- return intel_hsw_common_init(codec, INTEL_GLK_VENDOR_NID);
+ return intel_hsw_common_init(codec, 0x0b, NULL, 0);
+}
+
+static int patch_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.
+ */
+ static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map));
+}
+
+static int patch_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.
+ */
+ static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ int ret;
+
+ ret = intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map));
+ if (!ret) {
+ struct hdmi_spec *spec = codec->spec;
+
+ spec->dyn_pcm_no_legacy = true;
+ }
+
+ return ret;
}
/* Intel Baytrail and Braswell; with eld notifier */
@@ -2699,7 +3052,7 @@
/* 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->display_power_control = 1;
codec->depop_delay = 0;
codec->auto_runtime_pm = 1;
@@ -2790,7 +3143,7 @@
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
- snd_hda_jack_detect_enable(codec, pin);
+ snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id);
return 0;
}
@@ -2969,6 +3322,7 @@
if (!spec)
return -ENOMEM;
+ spec->codec = codec;
codec->spec = spec;
hdmi_array_init(spec, 1);
@@ -3180,6 +3534,7 @@
spec->pcm_playback.rates = SUPPORTED_RATES;
spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
spec->pcm_playback.formats = SUPPORTED_FORMATS;
+ spec->nv_dp_workaround = true;
return 0;
}
@@ -3273,7 +3628,62 @@
return 0;
}
+/* map from pin NID to port; port is 0-based */
+/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
+static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid - 4;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int nvhdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port + 4;
+}
+
+static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
+ .pin2port = nvhdmi_pin2port,
+ .pin_eld_notify = generic_acomp_pin_eld_notify,
+ .master_bind = generic_acomp_master_bind,
+ .master_unbind = generic_acomp_master_unbind,
+};
+
static int patch_nvhdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ codec->dp_mst = true;
+
+ spec = codec->spec;
+ spec->dyn_pcm_assign = true;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ generic_hdmi_init_per_pins(codec);
+
+ spec->dyn_pin_out = true;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ codec->link_down_at_suspend = 1;
+
+ generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
+
+ return 0;
+}
+
+static int patch_nvhdmi_legacy(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err;
@@ -3288,6 +3698,7 @@
spec->chmap.ops.chmap_cea_alloc_validate_get_type =
nvhdmi_chmap_cea_alloc_validate_get_type;
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
codec->link_down_at_suspend = 1;
@@ -3455,11 +3866,13 @@
if (err)
return err;
+ codec->depop_delay = 10;
codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
spec = codec->spec;
spec->chmap.ops.chmap_cea_alloc_validate_get_type =
nvhdmi_chmap_cea_alloc_validate_get_type;
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
return 0;
}
@@ -3512,16 +3925,19 @@
#define ATI_HBR_ENABLE 0x10
static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
- unsigned char *buf, int *eld_size)
+ int dev_id, unsigned char *buf, int *eld_size)
{
+ WARN_ON(dev_id != 0);
/* call hda_eld.c ATI/AMD-specific function */
return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
is_amdhdmi_rev3_or_later(codec));
}
-static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id, int ca,
int active_channels, int conn_type)
{
+ WARN_ON(dev_id != 0);
snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
}
@@ -3593,7 +4009,7 @@
static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
int verb;
int ati_channel_setup = 0;
@@ -3629,7 +4045,7 @@
static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
hda_nid_t pin_nid, int asp_slot)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
bool was_odd = false;
int ati_asp_slot = asp_slot;
int verb;
@@ -3712,9 +4128,11 @@
}
static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
- bool hbr)
+ int dev_id, bool hbr)
{
int hbr_ctl, hbr_ctl_new;
+
+ WARN_ON(dev_id != 0);
hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
@@ -3741,9 +4159,9 @@
}
static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
- hda_nid_t pin_nid, u32 stream_tag, int format)
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
{
-
if (is_amdhdmi_rev3_or_later(codec)) {
int ramp_rate = 180; /* default as per AMD spec */
/* disable ramp-up/down for non-pcm as per AMD spec */
@@ -3753,7 +4171,8 @@
snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
}
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
}
@@ -3779,9 +4198,30 @@
ATI_VERB_SET_MULTICHANNEL_MODE,
ATI_MULTICHANNEL_MODE_SINGLE);
}
+ codec->auto_runtime_pm = 1;
return 0;
}
+
+/* map from pin NID to port; port is 0-based */
+/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
+static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid / 2 - 1;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int atihdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port * 2 + 3;
+}
+
+static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
+ .pin2port = atihdmi_pin2port,
+ .pin_eld_notify = generic_acomp_pin_eld_notify,
+ .master_bind = generic_acomp_master_bind,
+ .master_unbind = generic_acomp_master_unbind,
+};
static int patch_atihdmi(struct hda_codec *codec)
{
@@ -3831,6 +4271,8 @@
*/
codec->link_down_at_suspend = 1;
+ generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
+
return 0;
}
@@ -3861,29 +4303,33 @@
HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
-HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy),
/* 17 is known to be absent */
-HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy),
HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi),
HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
@@ -3933,6 +4379,7 @@
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_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_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),
@@ -3946,7 +4393,15 @@
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi),
HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
-HDA_CODEC_ENTRY(0x80862800, "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(0x80862819, "DG2 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),
+HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
--
Gitblit v1.6.2