Parcourir la source

disp: msm: dp: ensure failsafe mode in connector mode list

The driver currently inserts a failsafe mode when EDID read fails
for SST. But for cases where the edid read succeeds but all the
modes are getting filtered out because of resource availability,
the driver does not add the failsafe mode. But the usermode
expects the failsafe mode to be always present in the mode list
as per DP specification. Also, the driver currently does not
add the failsafe mode, if the edid read fails on an MST monitor.

This change covers all these missing cases and makes sure the
failsafe mode is always in the connector's mode list if it is
in connected state.

Change-Id: I92eeaa00ad7b26a18b3689aa1c2ada4244aba3bc
Signed-off-by: Rajkumar Subbiah <[email protected]>
Rajkumar Subbiah il y a 2 ans
Parent
commit
ff99320123
5 fichiers modifiés avec 85 ajouts et 44 suppressions
  1. 8 1
      msm/dp/dp_display.c
  2. 53 19
      msm/dp/dp_drm.c
  3. 14 0
      msm/dp/dp_drm.h
  4. 9 3
      msm/dp/dp_mst_drm.c
  5. 1 21
      msm/dp/dp_panel.c

+ 8 - 1
msm/dp/dp_display.c

@@ -3040,6 +3040,12 @@ static enum drm_mode_status dp_display_validate_mode(
 
 	dp_display->convert_to_dp_mode(dp_display, panel, mode, &dp_mode);
 
+	/* As per spec, 640x480 mode should always be present as fail-safe */
+	if ((dp_mode.timing.h_active == 640) && (dp_mode.timing.v_active == 480) &&
+			(dp_mode.timing.pixel_clk_khz == 25175)) {
+		goto skip_validation;
+	}
+
 	rc = dp_display_validate_topology(dp, dp_panel, mode, &dp_mode, avail_res);
 	if (rc == -EAGAIN) {
 		dp_panel->convert_to_dp_mode(dp_panel, mode, &dp_mode);
@@ -3057,6 +3063,7 @@ static enum drm_mode_status dp_display_validate_mode(
 	if (rc)
 		goto end;
 
+skip_validation:
 	mode_status = MODE_OK;
 
 	if (!avail_res->num_lm_in_use) {
@@ -3070,7 +3077,7 @@ static enum drm_mode_status dp_display_validate_mode(
 end:
 	mutex_unlock(&dp->session_lock);
 
-	DP_DEBUG_V("[%s] mode is %s\n", mode->name,
+	DP_DEBUG_V("[%s clk:%d] mode is %s\n", mode->name, mode->clock,
 			(mode_status == MODE_OK) ? "valid" : "invalid");
 
 	return mode_status;

+ 53 - 19
msm/dp/dp_drm.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
@@ -308,6 +308,47 @@ static const struct drm_bridge_funcs dp_bridge_ops = {
 	.mode_set     = dp_bridge_mode_set,
 };
 
+int dp_connector_add_custom_mode(struct drm_connector *conn, struct dp_display_mode *dp_mode)
+{
+	struct drm_display_mode *m, drm_mode;
+
+	memset(&drm_mode, 0x0, sizeof(drm_mode));
+	convert_to_drm_mode(dp_mode, &drm_mode);
+	m = drm_mode_duplicate(conn->dev, &drm_mode);
+	if (!m) {
+		DP_ERR("failed to add mode %ux%u\n", drm_mode.hdisplay, drm_mode.vdisplay);
+		return 0;
+	}
+	m->width_mm = conn->display_info.width_mm;
+	m->height_mm = conn->display_info.height_mm;
+	drm_mode_probed_add(conn, m);
+
+	return 1;
+}
+
+void init_failsafe_mode(struct dp_display_mode *dp_mode)
+{
+	static const struct dp_panel_info fail_safe = {
+		.h_active = 640,
+		.v_active = 480,
+		.h_back_porch = 48,
+		.h_front_porch = 16,
+		.h_sync_width = 96,
+		.h_active_low = 1,
+		.v_back_porch = 33,
+		.v_front_porch = 10,
+		.v_sync_width = 2,
+		.v_active_low = 1,
+		.h_skew = 0,
+		.refresh_rate = 60,
+		.pixel_clk_khz = 25175,
+		.bpp = 24,
+		.widebus_en = true,
+	};
+
+	memcpy(&dp_mode->timing, &fail_safe, sizeof(fail_safe));
+}
+
 int dp_connector_config_hdr(struct drm_connector *connector, void *display,
 	struct sde_connector_state *c_state)
 {
@@ -558,7 +599,6 @@ int dp_connector_get_modes(struct drm_connector *connector,
 	int rc = 0;
 	struct dp_display *dp;
 	struct dp_display_mode *dp_mode = NULL;
-	struct drm_display_mode *m, drm_mode;
 	struct sde_connector *sde_conn;
 
 	if (!connector || !display)
@@ -578,25 +618,19 @@ int dp_connector_get_modes(struct drm_connector *connector,
 
 	/* pluggable case assumes EDID is read when HPD */
 	if (dp->is_sst_connected) {
+		/*
+		 * 1. for test request, rc = 1, and dp_mode will have test mode populated
+		 * 2. During normal operation, dp_mode will be untouched
+		 *    a. if mode query succeeds rc >= 0, valid modes will be added to connector
+		 *    b. if edid read failed, then connector mode list will be empty and rc <= 0
+		 */
 		rc = dp->get_modes(dp, sde_conn->drv_panel, dp_mode);
-		if (!rc)
-			DP_ERR("failed to get DP sink modes, rc=%d\n", rc);
-
-		if (dp_mode->timing.pixel_clk_khz) { /* valid DP mode */
-			memset(&drm_mode, 0x0, sizeof(drm_mode));
-			convert_to_drm_mode(dp_mode, &drm_mode);
-			m = drm_mode_duplicate(connector->dev, &drm_mode);
-			if (!m) {
-				DP_ERR("failed to add mode %ux%u\n",
-				       drm_mode.hdisplay,
-				       drm_mode.vdisplay);
-				kfree(dp_mode);
-				return 0;
-			}
-			m->width_mm = connector->display_info.width_mm;
-			m->height_mm = connector->display_info.height_mm;
-			drm_mode_probed_add(connector, m);
+		if (!rc) {
+			DP_WARN("failed to get DP sink modes, adding failsafe");
+			init_failsafe_mode(dp_mode);
 		}
+		if (dp_mode->timing.pixel_clk_khz) /* valid DP mode */
+			rc = dp_connector_add_custom_mode(connector, dp_mode);
 	} else {
 		DP_ERR("No sink connected\n");
 	}

+ 14 - 0
msm/dp/dp_drm.h

@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
@@ -168,6 +169,19 @@ int dp_connector_update_pps(struct drm_connector *connector,
 int dp_connector_install_properties(void *display,
 		struct drm_connector *conn);
 
+/**
+ * init_failsafe_mode - add failsafe edid mode
+ * @dp_mode: Pointer to mode
+ */
+void init_failsafe_mode(struct dp_display_mode *dp_mode);
+
+/**
+ * dp_connector_add_custom_mode - add edid mode to connector
+ * @conn: Pointer to connector
+ * @dp_mode: Pointer to mode
+ */
+int dp_connector_add_custom_mode(struct drm_connector *conn, struct dp_display_mode *dp_mode);
+
 #else
 static inline int dp_connector_config_hdr(struct drm_connector *connector,
 		void *display, struct sde_connector_state *c_state)

+ 9 - 3
msm/dp/dp_mst_drm.c

@@ -1178,6 +1178,7 @@ 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 dp_display_mode *dp_mode = NULL;
 	int rc = 0;
 	struct edid *edid = NULL;
 
@@ -1195,7 +1196,7 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
 			&mst->mst_mgr, c_conn->mst_port);
 
 	if (!edid) {
-		DP_ERR("get edid failed. id: %d\n", connector->base.id);
+		DP_WARN("get edid failed. id: %d\n", connector->base.id);
 		goto end;
 	}
 
@@ -1220,8 +1221,13 @@ duplicate_edid:
 end:
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id, rc);
 	if (rc <= 0) {
-		DP_ERR("conn:%d has no modes, rc=%d\n", connector->base.id, rc);
-		rc = 0;
+		DP_WARN("conn:%d has no modes, adding failsafe. rc=%d\n", connector->base.id, rc);
+		dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
+		if (!dp_mode)
+			return 0;
+
+		init_failsafe_mode(dp_mode);
+		rc = dp_connector_add_custom_mode(connector, dp_mode);
 	} else {
 		DP_MST_INFO("conn:%d has %d modes\n", connector->base.id, rc);
 	}

+ 1 - 21
msm/dp/dp_panel.c

@@ -84,23 +84,6 @@ struct dp_panel_private {
 	u8 minor;
 };
 
-static const struct dp_panel_info fail_safe = {
-	.h_active = 640,
-	.v_active = 480,
-	.h_back_porch = 48,
-	.h_front_porch = 16,
-	.h_sync_width = 96,
-	.h_active_low = 0,
-	.v_back_porch = 33,
-	.v_front_porch = 10,
-	.v_sync_width = 2,
-	.v_active_low = 0,
-	.h_skew = 0,
-	.refresh_rate = 60,
-	.pixel_clk_khz = 25200,
-	.bpp = 24,
-};
-
 /* OEM NAME */
 static const u8 vendor_name[8] = {81, 117, 97, 108, 99, 111, 109, 109};
 
@@ -2060,10 +2043,7 @@ static int dp_panel_get_modes(struct dp_panel *dp_panel,
 		return _sde_edid_update_modes(connector, dp_panel->edid_ctrl);
 	}
 
-	/* fail-safe mode */
-	memcpy(&mode->timing, &fail_safe,
-		sizeof(fail_safe));
-	return 1;
+	return 0;
 }
 
 static void dp_panel_handle_sink_request(struct dp_panel *dp_panel)