Like the previous patches. While at it also kill a stale comment - we've moved hdmi audio detection from ->get_modes to ->detect and the audio property handling functions. v2: Fixup use-after-free in edid caching because I've missed a kfree somewhere. Dunno how that one escape, because I clearly remember fixing this while testing the patch :( v3: Really fix this up. Signed-Off-by: Daniel Vetter <daniel.vetter at ffwll.ch> --- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 49 ++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d073623..ddb60bc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -303,6 +303,7 @@ struct intel_hdmi { struct dip_infoframe *frame); void (*set_infoframes)(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode); + struct edid *cached_edid; }; static inline struct drm_crtc * diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 69637db..d3dbc32 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -689,22 +689,40 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) return I915_READ(PORT_HOTPLUG_STAT) & bit; } +struct edid * +intel_hdmi_get_edid(struct drm_connector *connector) +{ + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + struct drm_i915_private *dev_priv = connector->dev->dev_private; + + if (!intel_hdmi->cached_edid) { + struct i2c_adapter *adapter; + + adapter = intel_gmbus_get_adapter(dev_priv, + intel_hdmi->ddc_bus); + intel_hdmi->cached_edid = drm_get_edid(connector, adapter); + } + + return intel_hdmi->cached_edid; +} + static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct drm_i915_private *dev_priv = connector->dev->dev_private; struct edid *edid; enum drm_connector_status status = connector_status_disconnected; if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi)) return status; + /* Clean the edid cache. */ + kfree(intel_hdmi->cached_edid); + intel_hdmi->cached_edid = NULL; + intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + edid = intel_hdmi_get_edid(connector); if (edid) { if (edid->input & DRM_EDID_INPUT_DIGITAL) { @@ -715,7 +733,6 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_audio = drm_detect_monitor_audio(edid); } connector->display_info.raw_edid = NULL; - kfree(edid); } if (status == connector_status_connected) { @@ -729,35 +746,24 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) static int intel_hdmi_get_modes(struct drm_connector *connector) { - struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct drm_i915_private *dev_priv = connector->dev->dev_private; - - /* We should parse the EDID data and find out if it's an HDMI sink so - * we can send audio to it. - */ + struct edid *edid; - return intel_ddc_get_modes(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + edid = intel_hdmi_get_edid(connector); + return intel_edid_get_modes(connector, edid); } static bool intel_hdmi_detect_audio(struct drm_connector *connector) { - struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct drm_i915_private *dev_priv = connector->dev->dev_private; struct edid *edid; bool has_audio = false; - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + edid = intel_hdmi_get_edid(connector); if (edid) { if (edid->input & DRM_EDID_INPUT_DIGITAL) has_audio = drm_detect_monitor_audio(edid); connector->display_info.raw_edid = NULL; - kfree(edid); } return has_audio; @@ -820,8 +826,11 @@ done: static void intel_hdmi_destroy(struct drm_connector *connector) { + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); + kfree(intel_hdmi->cached_edid); kfree(connector); } -- 1.7.7.6