[PATCH 3/4] drm/i915: cache hdmi edid

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux