diff mbox

[2/2] drm/i915: Optimize EDID retrival on detect and get_modes

Message ID 1389595914-23141-3-git-send-email-ramalingam.c@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ramalingam C Jan. 13, 2014, 6:51 a.m. UTC
From: Shashank Sharma <shashank.sharma@intel.com>

Multiple forced detect and read modes calls come from
user space for different connectors, which can be handled
with connector status and previous detect event's cached EDID.
With this approach for hot pluggable displays, EDID retrieval
is required only when there is a real hot-plug events.

This approach optimizes access to the DDC interface and also the
CPU cycles burned by intel_hdmi_detect and intel_hdmi_get_modes.

This patch contains:
1. A logic to optimize those multiple calls, by re-using
   cached data from previous detect and get_mode calls.
2. Store HDMI EDID, and re-use it in read modes.
3. Read live status reg to suppress spurious interrupts

Change-Id: Ia46cbe346dcc18ef11381eabcb157c5bcfd51322
Signed-off-by: Shashank Sharma <shashank.sharma@intel.com>
Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
 drivers/gpu/drm/i915/intel_drv.h  |    2 ++
 drivers/gpu/drm/i915/intel_hdmi.c |   66 +++++++++++++++++++++++++++++++++----
 2 files changed, 61 insertions(+), 7 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 0f7d94b..4f7f81f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -463,6 +463,8 @@  struct intel_hdmi {
 	bool has_audio;
 	enum hdmi_force_audio force_audio;
 	bool rgb_quant_range_selectable;
+	struct edid *edid;
+	uint32_t edid_mode_count;
 	void (*write_infoframe)(struct drm_encoder *encoder,
 				enum hdmi_infoframe_type type,
 				const void *frame, ssize_t len);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index faeae3a..2d008b7 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -994,7 +994,14 @@  intel_hdmi_detect(struct drm_connector *connector, bool force)
 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 		      connector->base.id, drm_get_connector_name(connector));
 
+	/* If its forced detect call, dont read EDID again */
+	if (force && intel_hdmi->edid)
+		return connector->status;
+
 	status = get_hdmi_hotplug_live_status(dev, intel_hdmi);
+	/* Suppress spurious IRQ, if current status is same as live status*/
+	if (connector->status == status)
+		return connector->status;
 
 	intel_hdmi->has_hdmi_sink = false;
 	intel_hdmi->has_audio = false;
@@ -1015,11 +1022,22 @@  intel_hdmi_detect(struct drm_connector *connector, bool force)
 						drm_detect_monitor_audio(edid);
 				intel_hdmi->rgb_quant_range_selectable =
 					drm_rgb_quant_range_selectable(edid);
+
+				/* Free previously saved EDID and save new one
+				   for read modes. */
+				kfree(intel_hdmi->edid);
+				intel_hdmi->edid = edid;
+			} else {
+				kfree(edid);
+				DRM_ERROR("EDID is not in digital form ?\n");
 			}
-			kfree(edid);
 		} else {
-			status = connector_status_disconnected;
+			DRM_DEBUG_KMS("EDID read failed\n");
 		}
+
+		if (intel_hdmi->edid == NULL)
+			status = connector_status_disconnected;
+
 	}
 
 	if (status == connector_status_connected) {
@@ -1027,6 +1045,9 @@  intel_hdmi_detect(struct drm_connector *connector, bool force)
 			intel_hdmi->has_audio =
 				(intel_hdmi->force_audio == HDMI_AUDIO_ON);
 		intel_encoder->type = INTEL_OUTPUT_HDMI;
+	} else {
+		kfree(intel_hdmi->edid);
+		intel_hdmi->edid = NULL;
 	}
 
 	return status;
@@ -1036,14 +1057,44 @@  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;
+	struct edid *edid = intel_hdmi->edid;
+	struct drm_display_mode *mode = NULL;
+	int count = 0;
 
-	/* We should parse the EDID data and find out if it's an HDMI sink so
-	 * we can send audio to it.
-	 */
+	/* No need to read modes if panel is not connected */
+	if (connector->status != connector_status_connected)
+		return 0;
 
-	return intel_ddc_get_modes(connector,
+	/* Need not read modes again if previously read modes are
+	   available and display is consistent */
+	if (intel_hdmi->edid) {
+		list_for_each_entry(mode, &connector->modes, head) {
+			if (mode) {
+				/* Setting the MODE_OK for all sanitized modes*/
+				mode->status = MODE_OK;
+				count++;
+			}
+		}
+		/* If modes are availlable, no need to read again */
+		if (count)
+			goto out;
+	}
+
+	/* EDID was saved in detect, re-use that if available, avoid
+	   reading EDID everytime */
+	if (edid) {
+		drm_mode_connector_update_edid_property(connector, edid);
+		count = drm_add_edid_modes(connector, edid);
+		drm_edid_to_eld(connector, edid);
+	} else {
+		count = intel_ddc_get_modes(connector,
 				   intel_gmbus_get_adapter(dev_priv,
-							   intel_hdmi->ddc_bus));
+						   intel_hdmi->ddc_bus));
+	}
+
+out:
+	intel_hdmi->edid_mode_count = count;
+	return count;
 }
 
 static bool
@@ -1381,6 +1432,7 @@  void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
 
 	intel_dig_port->port = port;
 	intel_dig_port->hdmi.hdmi_reg = hdmi_reg;
+	intel_dig_port->hdmi.edid = NULL;
 	intel_dig_port->dp.output_reg = 0;
 
 	intel_hdmi_init_connector(intel_dig_port, intel_connector);