@@ -1225,6 +1225,8 @@ struct dpcd_quirk {
static const struct dpcd_quirk dpcd_quirk_list[] = {
/* Analogix 7737 needs reduced M and N at HBR2 link rates */
{ OUI(0x00, 0x22, 0xb9), true, BIT(DP_DPCD_QUIRK_LIMITED_M_N) },
+ /* Samsung eDP panel */
+ { OUI(0xba, 0x41, 0x59), false, BIT(DP_DPCD_QUIRK_CUSTOMIZE_BRIGHTNESS_CONTROL) },
};
#undef OUI
@@ -1238,7 +1240,7 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
* to device identification string and hardware/firmware revisions later.
*/
static u32
-drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
+drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch, u8 *tcon_cap)
{
const struct dpcd_quirk *quirk;
u32 quirks = 0;
@@ -1253,6 +1255,11 @@ drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0)
continue;
+ if (quirk->quirks == DP_DPCD_QUIRK_CUSTOMIZE_BRIGHTNESS_CONTROL &&
+ (!(tcon_cap[1] & DP_BRIGHTNESS_CONTROL_NITS) ||
+ !(tcon_cap[2] & DP_BRIGHTNESS_CONTROL_BY_AUX)))
+ continue;
+
quirks |= quirk->quirks;
}
@@ -1276,12 +1283,19 @@ int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
struct drm_dp_dpcd_ident *ident = &desc->ident;
unsigned int offset = is_branch ? DP_BRANCH_OUI : DP_SINK_OUI;
int ret, dev_id_len;
+ u8 tcon_cap[4] = {0};
ret = drm_dp_dpcd_read(aux, offset, ident, sizeof(*ident));
if (ret < 0)
return ret;
- desc->quirks = drm_dp_get_quirks(ident, is_branch);
+ if (offset == DP_SINK_OUI) {
+ ret = drm_dp_dpcd_read(aux, DP_EDP_TCON_CAPABILITY_BYTE0, tcon_cap, sizeof(tcon_cap));
+ if (ret < 0)
+ return ret;
+ }
+
+ desc->quirks = drm_dp_get_quirks(ident, is_branch, tcon_cap);
dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id));
@@ -5934,6 +5934,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
goto out_vdd_off;
}
+ if (drm_dp_has_quirk(&intel_dp->desc,
+ DP_DPCD_QUIRK_CUSTOMIZE_BRIGHTNESS_CONTROL)) {
+ i915_modparams.enable_dpcd_backlight = true;
+ i915_modparams.fastboot = false;
+ DRM_DEBUG_KMS("Using specific DPCD to control brightness\n");
+ }
+
mutex_lock(&dev->mode_config.mutex);
edid = drm_get_edid(connector, &intel_dp->aux.ddc);
if (edid) {
@@ -24,6 +24,111 @@
#include "intel_drv.h"
+#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, DP_EDP_BRIGHTNESS_NITS,
+ &read_val, sizeof(read_val)) < 0) {
+ DRM_DEBUG_KMS("Failed to read DPCD register %x\n",
+ DP_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, DP_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, DP_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, DP_EDP_BRIGHTNESS_OPTIMIZATION, 0x01) < 0)
+ DRM_DEBUG_KMS("Failed to write %x\n",
+ DP_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, DP_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+ DRM_DEBUG_KMS("Failed to read %x\n",
+ DP_EDP_GETSET_CTRL_PARAMS);
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_GETSET_CTRL_PARAMS, 0) != 1)
+ DRM_DEBUG_KMS("Failed to write %x\n",
+ DP_EDP_GETSET_CTRL_PARAMS);
+
+ if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+ DRM_DEBUG_KMS("Failed to read %x\n",
+ DP_EDP_GETSET_CTRL_PARAMS);
+
+ if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_CONTENT_LUMINANCE, &read_val, sizeof(read_val)) < 0)
+ DRM_DEBUG_KMS("Failed to read %x\n",
+ DP_EDP_CONTENT_LUMINANCE);
+
+ memset(read_val, 0x0, 4);
+ if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_CONTENT_LUMINANCE, read_val, sizeof(read_val)) < 0)
+ DRM_DEBUG_KMS("Failed to write %x\n",
+ DP_EDP_CONTENT_LUMINANCE);
+
+ if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+ DRM_DEBUG_KMS("Failed to read %x\n",
+ DP_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)
{
uint8_t reg_val = 0;
@@ -197,6 +302,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 u32 intel_dp_aux_calc_max_backlight(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -308,6 +426,7 @@ intel_dp_aux_display_control_capable(struct intel_connector *connector)
int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
{
struct intel_panel *panel = &intel_connector->panel;
+ struct intel_dp *intel_dp = enc_to_intel_dp(&intel_connector->encoder->base);
if (!i915_modparams.enable_dpcd_backlight)
return -ENODEV;
@@ -315,11 +434,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 (drm_dp_has_quirk(&intel_dp->desc,
+ DP_DPCD_QUIRK_CUSTOMIZE_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;
}
@@ -625,6 +625,20 @@
/* up to ID_SLOT_63 at 0x2ff */
#define DP_SOURCE_OUI 0x300
+#define DP_EDP_TCON_CAPABILITY_BYTE0 0x340
+
+#define DP_EDP_TCON_CAPABILITY_BYTE1 0x341
+#define DP_BRIGHTNESS_CONTROL_NITS 0x10
+
+#define DP_EDP_TCON_CAPABILITY_BYTE2 0x342
+#define DP_BRIGHTNESS_CONTROL_BY_AUX 0x01
+
+#define DP_EDP_GETSET_CTRL_PARAMS 0x344
+#define DP_EDP_CONTENT_LUMINANCE 0x346
+#define DP_EDP_PANEL_LUMINANCE_OVERRIDE 0x34A
+#define DP_EDP_BRIGHTNESS_NITS 0x354
+#define DP_EDP_BRIGHTNESS_OPTIMIZATION 0x358
+
#define DP_SINK_OUI 0x400
#define DP_BRANCH_OUI 0x500
#define DP_BRANCH_ID 0x503
@@ -1161,6 +1175,12 @@ enum drm_dp_quirk {
* to 16 bits.
*/
DP_DPCD_QUIRK_LIMITED_M_N,
+ /**
+ * @DP_DPCD_QUIRK_NON_STANDARD_BRIGHTNESS_CONTROL:
+ *
+ * The device used specific DPCD register to control brightness.
+ */
+ DP_DPCD_QUIRK_CUSTOMIZE_BRIGHTNESS_CONTROL,
};
/**