[PATCH] drm/i915: customize DPCD brightness control for specific panel

Lee Shawn C shawn.c.lee at intel.com
Fri Oct 4 21:58:51 UTC 2019


This panel (manufacturer is SDC, product ID is 0x4141)
used manufacturer defined DPCD register to control brightness
that not defined in eDP spec so far. This change follow panel
vendor's instruction to support brightness adjustment.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=97883

Cc: Jani Nikula <jani.nikula at intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
Cc: Gustavo Padovan <gustavo at padovan.org>
Cc: Cooper Chiou <cooper.chiou at intel.com>
Signed-off-by: Lee Shawn C <shawn.c.lee at intel.com>
---
 drivers/gpu/drm/drm_edid.c                    |   6 +-
 drivers/gpu/drm/i915/display/intel_dp.c       |   7 +
 .../drm/i915/display/intel_dp_aux_backlight.c | 143 +++++++++++++++++-
 include/drm/drm_edid.h                        |   3 +
 4 files changed, 153 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 3c9703b08491..5aee0ebc200e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -211,6 +211,9 @@ static const struct edid_quirk {
 
 	/* OSVR HDK and HDK2 VR Headsets */
 	{ "SVR", 0x1019, EDID_QUIRK_NON_DESKTOP },
+
+	/* Samsung eDP panel */
+	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },
 };
 
 /*
@@ -1938,7 +1941,7 @@ static bool edid_vendor(const struct edid *edid, const char *vendor)
  *
  * This tells subsequent routines what fixes they need to apply.
  */
-static u32 edid_get_quirks(const struct edid *edid)
+u32 edid_get_quirks(const struct edid *edid)
 {
 	const struct edid_quirk *quirk;
 	int i;
@@ -1953,6 +1956,7 @@ static u32 edid_get_quirks(const struct edid *edid)
 
 	return 0;
 }
+EXPORT_SYMBOL(edid_get_quirks);
 
 #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
 #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 2b1e71f992b0..89193bd2d8ea 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -7097,6 +7097,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 	}
 	intel_connector->edid = edid;
 
+	if (edid_get_quirks(intel_connector->edid) ==
+	    EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL) {
+		i915_modparams.enable_dpcd_backlight = true;
+		i915_modparams.fastboot = false;
+		DRM_DEBUG_KMS("Using specific DPCD to control brightness\n");
+	}
+
 	fixed_mode = intel_panel_edid_fixed_mode(intel_connector);
 	if (fixed_mode)
 		downclock_mode = intel_dp_drrs_init(intel_connector, fixed_mode);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
index 020422da2ae2..7d9a2249cfb1 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -25,6 +25,117 @@
 #include "intel_display_types.h"
 #include "intel_dp_aux_backlight.h"
 
+#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
+#define DPCD_EDP_CONTENT_LUMINANCE		0x346
+#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
+#define DPCD_EDP_BRIGHTNESS_NITS		0x354
+#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
+
+#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)
+
+static uint32_t intel_dp_aux_get_customize_backlight(struct intel_connector *connector)
+{
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	uint8_t read_val[2] = { 0x0 };
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_NITS,
+			     &read_val, sizeof(read_val)) < 0) {
+		DRM_DEBUG_KMS("Failed to read DPCD register %x\n",
+			DPCD_EDP_BRIGHTNESS_NITS);
+		return 0;
+	}
+
+	return (read_val[1] << 8 | read_val[0]);
+}
+
+static void
+intel_dp_aux_set_customize_backlight(const struct drm_connector_state *conn_state, u32 level)
+{
+	struct intel_connector *connector = to_intel_connector(conn_state->connector);
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	struct intel_panel *panel = &connector->panel;
+	uint8_t new_vals[4];
+
+	if (level > panel->backlight.max)
+		level = panel->backlight.max;
+
+	new_vals[0] = level & 0xFF;
+	new_vals[1] = (level & 0xFF00) >> 8;
+	new_vals[2] = 0;
+	new_vals[3] = 0;
+
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_NITS, new_vals, 4) < 0)
+		DRM_DEBUG_KMS("Failed to write aux backlight level\n");
+}
+
+static void intel_dp_aux_enable_customize_backlight(const struct intel_crtc_state *crtc_state,
+					  const struct drm_connector_state *conn_state)
+{
+	struct intel_connector *connector = to_intel_connector(conn_state->connector);
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	uint8_t read_val[4], i;
+	uint8_t write_val[8] = {0x00, 0x00, 0xF0, 0x01, 0x90, 0x01, 0x00, 0x00};
+
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_PANEL_LUMINANCE_OVERRIDE, write_val, sizeof(write_val)) < 0)
+		DRM_DEBUG_KMS("Failed to write panel luminance.\n");
+
+	if (drm_dp_dpcd_writeb(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_OPTIMIZATION, 0x01) < 0)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_BRIGHTNESS_OPTIMIZATION);
+	/* write source OUI */
+	write_val[0] = 0x00;
+	write_val[1] = 0xaa;
+	write_val[2] = 0x01;
+	if (drm_dp_dpcd_write(&intel_dp->aux, DP_SOURCE_OUI, write_val, 3) < 0)
+		DRM_DEBUG_KMS("Failed to write OUI\n");
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_writeb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, 0) != 1)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DPCD_EDP_CONTENT_LUMINANCE, &read_val, sizeof(read_val)) < 0)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_CONTENT_LUMINANCE);
+
+	memset(read_val, 0x0, 4);
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_CONTENT_LUMINANCE, read_val, sizeof(read_val)) < 0)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_CONTENT_LUMINANCE);
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DP_SOURCE_OUI, read_val, 4) < 0)
+		DRM_DEBUG_KMS("Failed to read OUI\n");
+
+	DRM_DEBUG_KMS("got OUI %3ph\n", read_val);
+
+	for (i = 0; i < 6; i++) {
+		intel_dp_aux_set_customize_backlight(conn_state, connector->panel.backlight.level);
+
+		msleep(60);
+		if (intel_dp_aux_get_customize_backlight(connector))
+			return;
+	}
+
+	DRM_DEBUG_KMS("Restore brightness may have problem.\n");
+}
+
+static void intel_dp_aux_disable_customize_backlight(const struct drm_connector_state *old_conn_state)
+{
+	// do nothing
+	return;
+}
+
 static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
 {
 	u8 reg_val = 0;
@@ -225,6 +336,19 @@ static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old
 	set_aux_backlight_enable(enc_to_intel_dp(old_conn_state->best_encoder), false);
 }
 
+static int intel_dp_aux_setup_customize_backlight(struct intel_connector *connector,
+					enum pipe pipe)
+{
+	struct intel_panel *panel = &connector->panel;
+
+	panel->backlight.max = EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL;
+	panel->backlight.min = 0;
+	panel->backlight.level = panel->backlight.get(connector);
+	panel->backlight.enabled = panel->backlight.level != 0;
+
+	return 0;
+}
+
 static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
 					enum pipe pipe)
 {
@@ -274,11 +398,20 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
 	if (!intel_dp_aux_display_control_capable(intel_connector))
 		return -ENODEV;
 
-	panel->backlight.setup = intel_dp_aux_setup_backlight;
-	panel->backlight.enable = intel_dp_aux_enable_backlight;
-	panel->backlight.disable = intel_dp_aux_disable_backlight;
-	panel->backlight.set = intel_dp_aux_set_backlight;
-	panel->backlight.get = intel_dp_aux_get_backlight;
+	if (edid_get_quirks(intel_connector->edid) ==
+	    EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL) {
+		panel->backlight.setup   = intel_dp_aux_setup_customize_backlight;
+		panel->backlight.enable  = intel_dp_aux_enable_customize_backlight;
+		panel->backlight.disable = intel_dp_aux_disable_customize_backlight;
+		panel->backlight.set = intel_dp_aux_set_customize_backlight;
+		panel->backlight.get = intel_dp_aux_get_customize_backlight;
+	} else {
+		panel->backlight.setup   = intel_dp_aux_setup_backlight;
+		panel->backlight.enable  = intel_dp_aux_enable_backlight;
+		panel->backlight.disable = intel_dp_aux_disable_backlight;
+		panel->backlight.set = intel_dp_aux_set_backlight;
+		panel->backlight.get = intel_dp_aux_get_backlight;
+	}
 
 	return 0;
 }
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b9719418c3d2..b73bc3ee071e 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -282,6 +282,8 @@ struct detailed_timing {
 
 #define DRM_ELD_CEA_SAD(mnl, sad)	(20 + (mnl) + 3 * (sad))
 
+#define EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL	(1 << 13)
+
 struct edid {
 	u8 header[8];
 	/* Vendor & product info */
@@ -500,4 +502,5 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name,
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
 					   int hsize, int vsize, int fresh,
 					   bool rb);
+u32 edid_get_quirks(const struct edid *edid);
 #endif /* __DRM_EDID_H__ */
-- 
2.17.1



More information about the dri-devel mailing list