瀏覽代碼

disp: msm: dp: cache edid data for mst

Currently, in SST mode, sink edid is read once after hpd
and reused on subsequent mode enumeration calls. But in
MST mode, there is no caching and the driver re-reads the
edid from the sink on every get_modes call for each monitor.
Each read takes more than 500ms causing unnecessary delays
during MST enumeration.

This change reads the edid once per hpd and uses the
cached data on subsequent calls.

Change-Id: I27545a44b9f9bd40000dde60735815f9c47fa54c
Signed-off-by: Sudarsan Ramesh <[email protected]>
Sudarsan Ramesh 4 年之前
父節點
當前提交
cf4877c2c3
共有 2 個文件被更改,包括 67 次插入9 次删除
  1. 64 9
      msm/dp/dp_mst_drm.c
  2. 3 0
      msm/sde/sde_connector.h

+ 64 - 9
msm/dp/dp_mst_drm.c

@@ -34,6 +34,8 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_dp_mst_helper.h>
 #include <drm/drm_fixed.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_dp_helper.h>
 
 #include "msm_drv.h"
 #include "msm_kms.h"
@@ -151,6 +153,7 @@ struct dp_mst_private {
 	const struct dp_drm_mst_fw_helper_ops *mst_fw_cbs;
 	struct dp_mst_sim_mode simulator;
 	struct mutex mst_lock;
+	struct mutex edid_lock;
 	enum dp_drv_state state;
 	bool mst_session_state;
 };
@@ -1379,23 +1382,50 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
 	struct sde_connector *c_conn = to_sde_connector(connector);
 	struct dp_display *dp_display = display;
 	struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
-	struct edid *edid;
 	int rc = 0;
+	struct edid *edid = NULL;
 
 	DP_MST_DEBUG("enter:\n");
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, connector->base.id);
 
-	edid = mst->mst_fw_cbs->get_edid(connector, &mst->mst_mgr,
-			c_conn->mst_port);
+	mutex_lock(&mst->edid_lock);
 
-	if (edid)
-		rc = dp_display->mst_connector_update_edid(dp_display,
-				connector, edid);
+	if (c_conn->cached_edid)
+		goto duplicate_edid;
 
-	DP_MST_DEBUG("mst connector get modes. id: %d\n", connector->base.id);
+	mutex_unlock(&mst->edid_lock);
 
-	DP_MST_DEBUG("exit:\n");
-	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id);
+	edid = mst->mst_fw_cbs->get_edid(connector,
+			&mst->mst_mgr, c_conn->mst_port);
+
+	if (!edid) {
+		DP_MST_DEBUG("get edid failed. id: %d\n",
+				connector->base.id);
+		goto end;
+	}
+
+	mutex_lock(&mst->edid_lock);
+	c_conn->cached_edid = edid;
+
+duplicate_edid:
+
+	edid = drm_edid_duplicate(c_conn->cached_edid);
+
+	mutex_unlock(&mst->edid_lock);
+
+	if (IS_ERR(edid)) {
+		rc = PTR_ERR(edid);
+		DP_MST_DEBUG("edid duplication failed. id: %d\n",
+				connector->base.id);
+		goto end;
+	}
+
+	rc = dp_display->mst_connector_update_edid(dp_display,
+			connector, edid);
+
+end:
+	DP_MST_DEBUG("exit: id: %d rc: %d\n", connector->base.id, rc);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id, rc);
 
 	return rc;
 }
@@ -1728,6 +1758,9 @@ static void dp_mst_connector_pre_destroy(struct drm_connector *connector,
 	DP_MST_DEBUG("enter:\n");
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, conn_id);
 
+	kfree(c_conn->cached_edid);
+	c_conn->cached_edid = NULL;
+
 	drm_dp_mst_put_port_malloc(c_conn->mst_port);
 
 	dp_display->mst_connector_uninstall(dp_display, connector);
@@ -2227,6 +2260,9 @@ static void dp_mst_display_hpd_irq(void *dp_display,
 	u8 esi[14];
 	unsigned int esi_res = DP_SINK_COUNT_ESI + 1;
 	bool handled;
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *conn;
+	struct sde_connector *c_conn;
 
 	if (info->mst_hpd_sim) {
 		if (mst->simulator.mst_state && (info->mst_sim_add_con ||
@@ -2269,6 +2305,22 @@ static void dp_mst_display_hpd_irq(void *dp_display,
 	if (handled) {
 		rc = drm_dp_dpcd_write(mst->caps.drm_aux, esi_res, &esi[1], 3);
 
+		if (esi[1] & DP_UP_REQ_MSG_RDY) {
+			drm_connector_list_iter_begin(dp->drm_dev, &conn_iter);
+			drm_for_each_connector_iter(conn, &conn_iter) {
+
+				c_conn = to_sde_connector(conn);
+				if (!c_conn->mst_port)
+					continue;
+
+				mutex_lock(&mst->edid_lock);
+				kfree(c_conn->cached_edid);
+				c_conn->cached_edid = NULL;
+				mutex_unlock(&mst->edid_lock);
+			}
+			drm_connector_list_iter_end(&conn_iter);
+		}
+
 		if (rc != 3)
 			DP_ERR("dpcd esi_res failed. rlen=%d\n", rc);
 	}
@@ -2354,6 +2406,7 @@ int dp_mst_init(struct dp_display *dp_display)
 	dp_mst.dp_display = dp_display;
 
 	mutex_init(&dp_mst.mst_lock);
+	mutex_init(&dp_mst.edid_lock);
 
 	ret = drm_dp_mst_topology_mgr_init(&dp_mst.mst_mgr, dev,
 					dp_mst.caps.drm_aux,
@@ -2386,6 +2439,7 @@ int dp_mst_init(struct dp_display *dp_display)
 
 error:
 	mutex_destroy(&dp_mst.mst_lock);
+	mutex_destroy(&dp_mst.edid_lock);
 	return ret;
 }
 
@@ -2410,6 +2464,7 @@ void dp_mst_deinit(struct dp_display *dp_display)
 	dp_mst.mst_initialized = false;
 
 	mutex_destroy(&mst->mst_lock);
+	mutex_destroy(&mst->edid_lock);
 
 	DP_MST_INFO("dp drm mst topology manager deinit completed\n");
 }

+ 3 - 0
msm/sde/sde_connector.h

@@ -481,6 +481,7 @@ struct sde_connector_dyn_hdr_metadata {
  * @hdr_capable: external hdr support present
  * @cmd_rx_buf: the return buffer of response of command transfer
  * @rx_len: the length of dcs command received buffer
+ * @cached_edid: cached edid data for the connector
  */
 struct sde_connector {
 	struct drm_connector base;
@@ -550,6 +551,8 @@ struct sde_connector {
 
 	u8 cmd_rx_buf[MAX_CMD_RECEIVE_SIZE];
 	int rx_len;
+
+	struct edid *cached_edid;
 };
 
 /**