aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/pci/hda/patch_hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r--sound/pci/hda/patch_hdmi.c83
1 files changed, 81 insertions, 2 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 41eaa89660c3..b8c8490e568b 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -42,6 +42,11 @@ 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;
int assigned;
@@ -160,6 +165,7 @@ struct hdmi_spec {
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 */
@@ -167,6 +173,7 @@ struct hdmi_spec {
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
@@ -1635,21 +1642,72 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
snd_hda_power_down_pm(codec);
}
+static void silent_stream_enable(struct hda_codec *codec,
+ 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);
+
+ mutex_lock(&per_pin->lock);
+
+ if (!per_pin->channels)
+ per_pin->channels = 2;
+
+ 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);
+
+ hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+
+ mutex_unlock(&per_pin->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)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld;
+ bool monitor_prev, monitor_next;
mutex_lock(&per_pin->lock);
eld->monitor_present = false;
+ 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);
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);
+
+ /*
+ * Power-up will call hdmi_present_sense, so the PM calls
+ * have to be done without mutex held.
+ */
+
+ 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) {
+ 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);
+ }
+ }
}
static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
@@ -1701,7 +1759,8 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
* 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;
/*
@@ -1803,11 +1862,19 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
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),
+ {}
+};
+
static int hdmi_parse_codec(struct hda_codec *codec)
{
+ 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, &start_nid);
if (!start_nid || nodes < 0) {
@@ -1815,6 +1882,11 @@ static int hdmi_parse_codec(struct hda_codec *codec)
return -EINVAL;
}
+ 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
@@ -2440,6 +2512,7 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
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,
@@ -2534,7 +2607,6 @@ static void generic_acomp_init(struct hda_codec *codec,
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
match_bound_vga, 0)) {
spec->acomp_registered = true;
- codec->bus->keep_power = 0;
}
}
@@ -2802,6 +2874,13 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
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);
}