[alsa-devel] [PATCH v2 0/4] i915 to call hda driver on HDMI plug/unplug
Changes since previous version:
* Renames and refactorings according to Daniel's and Takashi's suggestions.
* Debug message slightly improved.
* Braswell now has all three pins too, Baytrail still has only one pin. It would still be good with some confirmation from Intel that this is correct.
David Henningsson (4): drm/i915: Add audio hotplug info struct drm/i915: Call audio hotplug notify function ALSA: hda - Dispatch incoming HDMI hotplug i915 callback ALSA: hda - Wake the codec up on hotplug notify events
drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_audio.c | 41 ++++++++++++++++++++++++++++++++++++ include/drm/i915_component.h | 18 ++++++++++++++++ include/sound/hdaudio.h | 4 ++++ sound/hda/hdac_i915.c | 26 +++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 13 +++++++++++- 6 files changed, 102 insertions(+), 1 deletion(-)
This struct will be used to transfer information from the i915 driver to the hda driver on HDMI hotplug events.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- include/drm/i915_component.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h index c9a8b64..d4c5648 100644 --- a/include/drm/i915_component.h +++ b/include/drm/i915_component.h @@ -24,8 +24,22 @@ #ifndef _I915_COMPONENT_H_ #define _I915_COMPONENT_H_
+struct hdac_bus; + +struct i915_audio_hotplug_info { + int connector_type; /* DRM_MODE_CONNECTOR_*, meant for userspace */ + int connector_type_id; /* Index within a DRM_MODE_CONNECTOR_* type, meant for userspace */ + int port; /* Used for mapping to affected nid */ + int port_multi_stream_device; /* For DP multi-streaming */ + + bool plugged_in; + const unsigned char *eld; + int eld_size; +}; + struct i915_audio_component { struct device *dev; + struct hdac_bus *hdac_bus;
const struct i915_audio_component_ops { struct module *owner; @@ -34,6 +48,10 @@ struct i915_audio_component { void (*codec_wake_override)(struct device *, bool enable); int (*get_cdclk_freq)(struct device *); } *ops; + + const struct i915_audio_component_audio_ops { + void (*hotplug_notify)(struct hdac_bus *, const struct i915_audio_hotplug_info *); + } *audio_ops; };
#endif /* _I915_COMPONENT_H_ */
On HDMI hotplug events, notify the audio driver. This will enable the audio driver to get the information at all times (even when audio is in different powersave states), and also without reading it from the hardware.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_audio.c | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 542fac6..696624c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1808,6 +1808,7 @@ struct drm_i915_private { struct drm_property *force_audio_property;
/* hda/i915 audio component */ + struct i915_audio_component *audio_component; bool audio_component_registered;
uint32_t hw_context_size; diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 3da9b84..2700521 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -384,6 +384,39 @@ static void ilk_audio_codec_enable(struct drm_connector *connector, I915_WRITE(aud_config, tmp); }
+static void audio_hotplug_notify(struct intel_encoder *intel_encoder, + struct drm_connector *connector) +{ + struct i915_audio_hotplug_info audio_info; + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_audio_component *acomp = dev_priv->audio_component; + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + enum port port = intel_dig_port->port; + + if (!acomp || !acomp->audio_ops || !acomp->audio_ops->hotplug_notify) + return; + + memset((void *) &audio_info, 0, sizeof(audio_info)); + + if (connector) { + audio_info.connector_type = connector->connector_type; + audio_info.connector_type_id = connector->connector_type_id; + audio_info.plugged_in = true; + audio_info.eld = connector->eld; + audio_info.eld_size = drm_eld_size(audio_info.eld); + } else { + audio_info.connector_type = -1; + audio_info.connector_type_id = -1; + } + + audio_info.port = (int) port; + /* DP Mst is unsupported for now */ + + acomp->audio_ops->hotplug_notify(acomp->hdac_bus, &audio_info); +} + /** * intel_audio_codec_enable - Enable the audio codec for HD audio * @intel_encoder: encoder on which to enable audio @@ -419,6 +452,8 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder)
if (dev_priv->display.audio_codec_enable) dev_priv->display.audio_codec_enable(connector, intel_encoder, mode); + + audio_hotplug_notify(intel_encoder, connector); }
/** @@ -435,6 +470,8 @@ void intel_audio_codec_disable(struct intel_encoder *encoder)
if (dev_priv->display.audio_codec_disable) dev_priv->display.audio_codec_disable(encoder); + + audio_hotplug_notify(encoder, NULL); }
/** @@ -525,12 +562,14 @@ static int i915_audio_component_bind(struct device *i915_dev, struct device *hda_dev, void *data) { struct i915_audio_component *acomp = data; + struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
if (WARN_ON(acomp->ops || acomp->dev)) return -EEXIST;
acomp->ops = &i915_audio_component_ops; acomp->dev = i915_dev; + dev_priv->audio_component = acomp;
return 0; } @@ -539,9 +578,11 @@ static void i915_audio_component_unbind(struct device *i915_dev, struct device *hda_dev, void *data) { struct i915_audio_component *acomp = data; + struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
acomp->ops = NULL; acomp->dev = NULL; + dev_priv->audio_component = NULL; }
static const struct component_ops i915_audio_component_bind_ops = {
This lets interested codec(s) be notified of HDMI hotplug events sent from the i915 driver.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- include/sound/hdaudio.h | 4 ++++ sound/hda/hdac_i915.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 4caf1fd..18a29ba 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -79,6 +79,10 @@ struct hdac_device { int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res);
+ /* Used for hotplug notification from i915 driver */ + void (*i915_hotplug_notify)(struct hdac_device *, + const struct i915_audio_hotplug_info *); + /* widgets */ unsigned int num_nodes; hda_nid_t start_nid, end_nid; diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 5676b84..7a54b76 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -118,6 +118,8 @@ static void hdac_component_master_unbind(struct device *dev) { struct i915_audio_component *acomp = hdac_acomp;
+ acomp->audio_ops = NULL; + acomp->hdac_bus = NULL; module_put(acomp->ops->owner); component_unbind_all(dev, acomp); WARN_ON(acomp->ops || acomp->dev); @@ -128,6 +130,27 @@ static const struct component_master_ops hdac_component_master_ops = { .unbind = hdac_component_master_unbind, };
+static void i915_audio_component_hotplug_notify(struct hdac_bus *bus, + const struct i915_audio_hotplug_info *info) +{ + struct hdac_device *d; + + if (info->plugged_in) + dev_dbg(bus->dev, "i915 plugged in event (port = %d, connector = %d,%d)", + info->port, info->connector_type, info->connector_type_id); + else + dev_dbg(bus->dev, "i915 unplug event (port = %d)", info->port); + + list_for_each_entry(d, &bus->codec_list, list) { + if (d->i915_hotplug_notify) + d->i915_hotplug_notify(d, info); + } +} + +static const struct i915_audio_component_audio_ops i915_audio_component_audio_ops = { + .hotplug_notify = i915_audio_component_hotplug_notify, +}; + static int hdac_component_master_match(struct device *dev, void *data) { /* i915 is the only supported component */ @@ -163,6 +186,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus) ret = -ENODEV; goto out_master_del; } + acomp->audio_ops = &i915_audio_component_audio_ops; + acomp->hdac_bus = bus; + dev_dbg(dev, "bound to i915 component master\n");
return 0;
As a first cautious step, we're not going to trust the information coming directly from the i915 driver, we're just going to use the fact that there was an event to wakeup the codec and ask for its status.
This fixes the issue with lost unsol events in power save mode, the codec and controller can now sleep in D3 and still know when the HDMI monitor has been connected.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/patch_hdmi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2f24338..870b6d2 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2316,6 +2316,15 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_set_power_to_all(codec, fg, power_state); }
+static void intel_hotplug_notify(struct hdac_device *dev, + const struct i915_audio_hotplug_info *info) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + int pin_nid = is_valleyview(codec) ? 0x03 : info->port + 0x04; + + check_presence_and_report(codec, pin_nid); +} + static int patch_generic_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -2342,8 +2351,10 @@ static int patch_generic_hdmi(struct hda_codec *codec) if (is_valleyview_plus(codec) || is_skylake(codec)) codec->core.link_power_control = 1;
- if (is_haswell_plus(codec) || is_valleyview_plus(codec)) + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { codec->depop_delay = 0; + codec->core.i915_hotplug_notify = intel_hotplug_notify; + }
if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL;
participants (1)
-
David Henningsson