2019-11-22 23:17:55

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 0/5] drm/i915: eDP DPCD aux backlight fixes

I recently got a ThinkPad X1 Extreme 2nd Generation for fixing some
issues on, and noticed that out of the box the backlight doesn't work.
Along the way of fixing that, I found a few issues with how i915 handles
DPCD AUX backlight control and fixed them. Now I've got working
backlight controls, hooray!

Note that this patch series also enables DPCD aux backlight control by
default based on what the VBT tells us.

Lyude Paul (5):
drm/i915: Fix eDP DPCD aux max backlight calculations
drm/i915: Assume 100% brightness when not in DPCD control mode
drm/i915: Fix DPCD register order in intel_dp_aux_enable_backlight()
drm/i915: Auto detect DPCD backlight support by default
drm/i915: Force DPCD backlight mode on X1 Extreme 2nd Gen 4K AMOLED
panel

drivers/gpu/drm/drm_dp_helper.c | 4 +
.../drm/i915/display/intel_display_types.h | 3 +
.../drm/i915/display/intel_dp_aux_backlight.c | 167 ++++++++++++------
drivers/gpu/drm/i915/i915_params.c | 2 +-
drivers/gpu/drm/i915/i915_params.h | 2 +-
include/drm/drm_dp_helper.h | 8 +
6 files changed, 134 insertions(+), 52 deletions(-)

--
2.21.0


2019-11-22 23:18:14

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 1/5] drm/i915: Fix eDP DPCD aux max backlight calculations

Max backlight value for the panel was being calculated using byte
count i.e. 0xffff if 2 bytes are supported for backlight brightness
and 0xff if 1 byte is supported. However, EDP_PWMGEN_BIT_COUNT
determines the number of active control bits used for the brightness
setting. Thus, even if the panel uses 2 byte setting, it might not use
all the control bits. Thus, max backlight should be set based on the
value of EDP_PWMGEN_BIT_COUNT instead of assuming 65535 or 255.

Additionally, EDP_PWMGEN_BIT_COUNT was being updated based on the VBT
frequency which results in a different max backlight value. Thus,
setting of EDP_PWMGEN_BIT_COUNT is moved to setup phase instead of
enable so that max backlight can be calculated correctly. Only the
frequency divider is set during the enable phase using the value of
EDP_PWMGEN_BIT_COUNT.

This is based off the original patch series from Furquan Shaikh
<[email protected]>:

https://patchwork.freedesktop.org/patch/317255/?series=62326&rev=3

Changes since original patch:
* Remove unused intel_dp variable in intel_dp_aux_setup_backlight()
* Fix checkpatch issues
* Make sure that we rewrite the pwmgen bit count whenever we bring the
panel out of D3 mode

Cc: Furquan Shaikh <[email protected]>
Signed-off-by: Lyude Paul <[email protected]>
---
.../drm/i915/display/intel_display_types.h | 3 +
.../drm/i915/display/intel_dp_aux_backlight.c | 139 ++++++++++++------
2 files changed, 95 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 83ea04149b77..2a8d8cae638e 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -214,6 +214,9 @@ struct intel_panel {
u8 controller; /* bxt+ only */
struct pwm_device *pwm;

+ /* DPCD backlight */
+ u8 pwmgen_bit_count;
+
struct backlight_device *device;

/* Connector and platform specific backlight functions */
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..fad470553cf9 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -111,61 +111,28 @@ static bool intel_dp_aux_set_pwm_freq(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
- int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
- u8 pn, pn_min, pn_max;
+ const u8 pn = connector->panel.backlight.pwmgen_bit_count;
+ int freq, fxp, f, fxp_actual, fxp_min, fxp_max;

- /* Find desired value of (F x P)
- * Note that, if F x P is out of supported range, the maximum value or
- * minimum value will applied automatically. So no need to check that.
- */
freq = dev_priv->vbt.backlight.pwm_freq_hz;
- DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
if (!freq) {
DRM_DEBUG_KMS("Use panel default backlight frequency\n");
return false;
}

fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
+ f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
+ fxp_actual = f << pn;

- /* Use highest possible value of Pn for more granularity of brightness
- * adjustment while satifying the conditions below.
- * - Pn is in the range of Pn_min and Pn_max
- * - F is in the range of 1 and 255
- * - FxP is within 25% of desired value.
- * Note: 25% is arbitrary value and may need some tweak.
- */
- if (drm_dp_dpcd_readb(&intel_dp->aux,
- DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
- DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
- return false;
- }
- if (drm_dp_dpcd_readb(&intel_dp->aux,
- DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
- DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
- return false;
- }
- pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
- pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
-
+ /* Ensure frequency is within 25% of desired value */
fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
- if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
- DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
- return false;
- }
-
- for (pn = pn_max; pn >= pn_min; pn--) {
- f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
- fxp_actual = f << pn;
- if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
- break;
- }

- if (drm_dp_dpcd_writeb(&intel_dp->aux,
- DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
- DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
+ if (fxp_min > fxp_actual || fxp_actual > fxp_max) {
+ DRM_DEBUG_KMS("Actual frequency out of range\n");
return false;
}
+
if (drm_dp_dpcd_writeb(&intel_dp->aux,
DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
DRM_DEBUG_KMS("Failed to write aux backlight freq\n");
@@ -178,6 +145,7 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
const struct drm_connector_state *conn_state)
{
struct intel_connector *connector = to_intel_connector(conn_state->connector);
+ struct intel_panel *panel = &connector->panel;
struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;

@@ -197,6 +165,12 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_EDP_PWMGEN_BIT_COUNT,
+ panel->backlight.pwmgen_bit_count) < 0)
+ DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
+
break;

/* Do nothing when it is already DPCD mode */
@@ -225,20 +199,91 @@ 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 u32 intel_dp_aux_calc_max_backlight(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+ struct intel_panel *panel = &connector->panel;
+ u32 max_backlight = 0;
+ int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
+ u8 pn, pn_min, pn_max;
+
+ if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn)) {
+ pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+ max_backlight = (1 << pn) - 1;
+ }
+
+ /* Find desired value of (F x P)
+ * Note that, if F x P is out of supported range, the maximum value or
+ * minimum value will applied automatically. So no need to check that.
+ */
+ freq = dev_priv->vbt.backlight.pwm_freq_hz;
+ DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
+ if (!freq) {
+ DRM_DEBUG_KMS("Use panel default backlight frequency\n");
+ return max_backlight;
+ }
+
+ fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
+
+ /* Use highest possible value of Pn for more granularity of brightness
+ * adjustment while satifying the conditions below.
+ * - Pn is in the range of Pn_min and Pn_max
+ * - F is in the range of 1 and 255
+ * - FxP is within 25% of desired value.
+ * Note: 25% is arbitrary value and may need some tweak.
+ */
+ if (drm_dp_dpcd_readb(&intel_dp->aux,
+ DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
+ DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
+ return max_backlight;
+ }
+ if (drm_dp_dpcd_readb(&intel_dp->aux,
+ DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
+ DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
+ return max_backlight;
+ }
+ pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+ pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+
+ fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
+ fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
+ if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
+ DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
+ return max_backlight;
+ }
+
+ for (pn = pn_max; pn >= pn_min; pn--) {
+ f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
+ fxp_actual = f << pn;
+ if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
+ break;
+ }
+
+ DRM_DEBUG_KMS("Using eDP pwmgen bit count of %d\n", pn);
+ if (drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
+ DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
+ return max_backlight;
+ }
+ panel->backlight.pwmgen_bit_count = pn;
+
+ max_backlight = (1 << pn) - 1;
+
+ return max_backlight;
+}
+
static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
enum pipe pipe)
{
- struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
struct intel_panel *panel = &connector->panel;

- if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
- panel->backlight.max = 0xFFFF;
- else
- panel->backlight.max = 0xFF;
+ panel->backlight.max = intel_dp_aux_calc_max_backlight(connector);
+ if (!panel->backlight.max)
+ return -ENODEV;

panel->backlight.min = 0;
panel->backlight.level = intel_dp_aux_get_backlight(connector);
-
panel->backlight.enabled = panel->backlight.level != 0;

return 0;
--
2.21.0

2019-11-22 23:18:35

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 2/5] drm/i915: Assume 100% brightness when not in DPCD control mode

Currently we always determine the initial panel brightness level by
simply reading the value from DP_EDP_BACKLIGHT_BRIGHTNESS_MSB/LSB. This
seems wrong though, because if the panel is not currently in DPCD
control mode there's not really any reason why there would be any
brightness value programmed in the first place.

This appears to be the case on the Lenovo ThinkPad X1 Extreme 2nd
Generation, where the default value in these registers is always 0 on
boot despite the fact the panel runs at max brightness by default.
Getting the initial brightness value correct here is important as well,
since the panel on this laptop doesn't behave well if it's ever put into
DPCD control mode while the brightness level is programmed to 0.

So, let's fix this by checking what the current backlight control mode
is before reading the brightness level. If it's in DPCD control mode, we
return the programmed brightness level. Otherwise we assume 100%
brightness and return the highest possible brightness level. This also
prevents us from accidentally programming a brightness level of 0.

This is one of the many fixes that gets backlight controls working on
the ThinkPad X1 Extreme 2nd Generation with optional 4K AMOLED screen.

Signed-off-by: Lyude Paul <[email protected]>
---
.../gpu/drm/i915/display/intel_dp_aux_backlight.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

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 fad470553cf9..0bf8772bc7bb 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -59,8 +59,23 @@ static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
u8 read_val[2] = { 0x0 };
+ u8 control_reg;
u16 level = 0;

+ if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
+ &control_reg) != 1) {
+ DRM_DEBUG_KMS("Failed to read the DPCD register 0x%x\n",
+ DP_EDP_DISPLAY_CONTROL_REGISTER);
+ return 0;
+ }
+
+ /*
+ * If we're not in DPCD control mode yet, the programmed brightness
+ * value is meaningless and we should assume max brightness
+ */
+ if (!(control_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD))
+ return connector->panel.backlight.max;
+
if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
&read_val, sizeof(read_val)) < 0) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
--
2.21.0

2019-11-22 23:18:59

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 4/5] drm/i915: Auto detect DPCD backlight support by default

Turns out we actually already have some companies, such as Lenovo,
shipping machines with AMOLED screens that don't allow controlling the
backlight through the usual PWM interface and only allow controlling it
through the standard EDP DPCD interface. One example of one of these
laptops is the X1 Extreme 2nd Generation.

Since we've got systems that need this turned on by default now to have
backlight controls working out of the box, let's start auto-detecting it
for systems by default based on what the VBT tells us. We do this by
changing the default value for the enable_dpcd_backlight module param
from 0 to -1.

Signed-off-by: Lyude Paul <[email protected]>
---
drivers/gpu/drm/i915/i915_params.c | 2 +-
drivers/gpu/drm/i915/i915_params.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
index 1dd1f3652795..31eed60c167e 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -172,7 +172,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400,

i915_param_named(enable_dpcd_backlight, int, 0600,
"Enable support for DPCD backlight control"
- "(-1=use per-VBT LFP backlight type setting, 0=disabled [default], 1=enabled)");
+ "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)");

#if IS_ENABLED(CONFIG_DRM_I915_GVT)
i915_param_named(enable_gvt, bool, 0400,
diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
index 31b88f297fbc..a79d0867f77a 100644
--- a/drivers/gpu/drm/i915/i915_params.h
+++ b/drivers/gpu/drm/i915/i915_params.h
@@ -64,7 +64,7 @@ struct drm_printer;
param(int, reset, 3) \
param(unsigned int, inject_probe_failure, 0) \
param(int, fastboot, -1) \
- param(int, enable_dpcd_backlight, 0) \
+ param(int, enable_dpcd_backlight, -1) \
param(char *, force_probe, CONFIG_DRM_I915_FORCE_PROBE) \
param(unsigned long, fake_lmem_start, 0) \
/* leave bools at the end to not create holes */ \
--
2.21.0

2019-11-22 23:21:04

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 3/5] drm/i915: Fix DPCD register order in intel_dp_aux_enable_backlight()

For eDP panels, it appears it's expected that so long as the panel is in
DPCD control mode that the brightness value is never set to 0. Instead,
if the desired effect is to set the panel's backlight to 0 we're
expected to simply turn off the backlight through the
DP_EDP_DISPLAY_CONTROL_REGISTER.

We already do the latter correctly in intel_dp_aux_disable_backlight().
But, we make the mistake of writing the DPCD registers in the wrong
order when enabling the backlight in intel_dp_aux_enable_backlight()
since we currently enable the backlight through
DP_EDP_DISPLAY_CONTROL_REGISTER before writing the brightness level. On
the X1 Extreme 2nd Generation, this appears to have the potential of
confusing the panel in such a way that further attempts to set the
brightness don't actually change the backlight as expected and leave it
off. Presumably, this happens because the incorrect register writing
order briefly leaves the panel with DPCD mode enabled and a 0 brightness
level set.

So, reverse the order we write the DPCD registers when enabling the
panel backlight so that we write the brightness value first, and enable
the backlight second. This fix appears to be the final bit needed to get
the backlight on the ThinkPad X1 Extreme 2nd Generation's AMOLED screen
working.

Signed-off-by: Lyude Paul <[email protected]>
---
drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

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 0bf8772bc7bb..87b59db9ffe3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -205,8 +205,9 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
}
}

+ intel_dp_aux_set_backlight(conn_state,
+ connector->panel.backlight.level);
set_aux_backlight_enable(intel_dp, true);
- intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
}

static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
--
2.21.0

2019-11-22 23:21:24

by Lyude Paul

[permalink] [raw]
Subject: [PATCH 5/5] drm/i915: Force DPCD backlight mode on X1 Extreme 2nd Gen 4K AMOLED panel

Annoyingly, the VBT on the ThinkPad X1 Extreme 2nd Gen indicates that
the system uses plain PWM based backlight controls, when in reality the
only backlight controls that work are the standard VESA eDP DPCD
backlight controls.

Honestly, this makes me wonder how many other systems have these issues
or lie about this in their VBT. Not sure we have any good way of finding
out until panels like this become more common place in the laptop
market. For now, just add a DRM DP quirk to indicate that this panel is
telling the truth and is being a good LCD.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=112376
Signed-off-by: Lyude Paul <[email protected]>
---
drivers/gpu/drm/drm_dp_helper.c | 4 ++++
drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 10 ++++++++--
include/drm/drm_dp_helper.h | 8 ++++++++
3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 2c7870aef469..ec7061e3a99b 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -1155,6 +1155,10 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
/* CH7511 seems to leave SINK_COUNT zeroed */
{ OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
+ /* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation
+ * only supports DPCD backlight controls, despite advertising otherwise
+ */
+ { OUI(0xba, 0x41, 0x59), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT) },
};

#undef OUI
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 87b59db9ffe3..3d61260b08ad 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -325,11 +325,17 @@ 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 drm_i915_private *dev_priv = to_i915(intel_connector->base.dev);
+ struct intel_dp *intel_dp =
+ enc_to_intel_dp(&intel_connector->encoder->base);
+ struct drm_i915_private *dev_priv =
+ to_i915(intel_connector->base.dev);

if (i915_modparams.enable_dpcd_backlight == 0 ||
(i915_modparams.enable_dpcd_backlight == -1 &&
- dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE))
+ dev_priv->vbt.backlight.type !=
+ INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
+ !drm_dp_has_quirk(&intel_dp->desc,
+ DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT)))
return -ENODEV;

if (!intel_dp_aux_display_control_capable(intel_connector))
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 51ecb5112ef8..a444209cd54b 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -1520,6 +1520,14 @@ enum drm_dp_quirk {
* The driver should ignore SINK_COUNT during detection.
*/
DP_DPCD_QUIRK_NO_SINK_COUNT,
+ /**
+ * @DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT:
+ *
+ * The device is telling the truth when it says that it uses DPCD
+ * backlight controls, even if the system's firmware disagrees.
+ * The driver should honor the DPCD backlight capabilities advertised.
+ */
+ DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT,
};

/**
--
2.21.0

2019-12-03 12:41:29

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH 2/5] drm/i915: Assume 100% brightness when not in DPCD control mode

On Fri, 22 Nov 2019, Lyude Paul <[email protected]> wrote:
> Currently we always determine the initial panel brightness level by
> simply reading the value from DP_EDP_BACKLIGHT_BRIGHTNESS_MSB/LSB. This
> seems wrong though, because if the panel is not currently in DPCD
> control mode there's not really any reason why there would be any
> brightness value programmed in the first place.
>
> This appears to be the case on the Lenovo ThinkPad X1 Extreme 2nd
> Generation, where the default value in these registers is always 0 on
> boot despite the fact the panel runs at max brightness by default.
> Getting the initial brightness value correct here is important as well,
> since the panel on this laptop doesn't behave well if it's ever put into
> DPCD control mode while the brightness level is programmed to 0.
>
> So, let's fix this by checking what the current backlight control mode
> is before reading the brightness level. If it's in DPCD control mode, we
> return the programmed brightness level. Otherwise we assume 100%
> brightness and return the highest possible brightness level. This also
> prevents us from accidentally programming a brightness level of 0.
>
> This is one of the many fixes that gets backlight controls working on
> the ThinkPad X1 Extreme 2nd Generation with optional 4K AMOLED screen.
>
> Signed-off-by: Lyude Paul <[email protected]>
> ---
> .../gpu/drm/i915/display/intel_dp_aux_backlight.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> 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 fad470553cf9..0bf8772bc7bb 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -59,8 +59,23 @@ static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
> {
> struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> u8 read_val[2] = { 0x0 };
> + u8 control_reg;
> u16 level = 0;
>
> + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,

Shouldn't that be DP_EDP_BACKLIGHT_MODE_SET_REGISTER instead?

> + &control_reg) != 1) {
> + DRM_DEBUG_KMS("Failed to read the DPCD register 0x%x\n",
> + DP_EDP_DISPLAY_CONTROL_REGISTER);
> + return 0;
> + }
> +
> + /*
> + * If we're not in DPCD control mode yet, the programmed brightness
> + * value is meaningless and we should assume max brightness
> + */
> + if (!(control_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD))
> + return connector->panel.backlight.max;

It's not just a bit, I think you need to check (control_reg & mask) ==
value.

BR,
Jani.

> +
> if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
> &read_val, sizeof(read_val)) < 0) {
> DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",

--
Jani Nikula, Intel Open Source Graphics Center

2019-12-03 12:42:02

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/i915: Fix DPCD register order in intel_dp_aux_enable_backlight()

On Fri, 22 Nov 2019, Lyude Paul <[email protected]> wrote:
> For eDP panels, it appears it's expected that so long as the panel is in
> DPCD control mode that the brightness value is never set to 0. Instead,
> if the desired effect is to set the panel's backlight to 0 we're
> expected to simply turn off the backlight through the
> DP_EDP_DISPLAY_CONTROL_REGISTER.
>
> We already do the latter correctly in intel_dp_aux_disable_backlight().
> But, we make the mistake of writing the DPCD registers in the wrong
> order when enabling the backlight in intel_dp_aux_enable_backlight()
> since we currently enable the backlight through
> DP_EDP_DISPLAY_CONTROL_REGISTER before writing the brightness level. On
> the X1 Extreme 2nd Generation, this appears to have the potential of
> confusing the panel in such a way that further attempts to set the
> brightness don't actually change the backlight as expected and leave it
> off. Presumably, this happens because the incorrect register writing
> order briefly leaves the panel with DPCD mode enabled and a 0 brightness
> level set.
>
> So, reverse the order we write the DPCD registers when enabling the
> panel backlight so that we write the brightness value first, and enable
> the backlight second. This fix appears to be the final bit needed to get
> the backlight on the ThinkPad X1 Extreme 2nd Generation's AMOLED screen
> working.
>
> Signed-off-by: Lyude Paul <[email protected]>

Reviewed-by: Jani Nikula <[email protected]>

> ---
> drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> 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 0bf8772bc7bb..87b59db9ffe3 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -205,8 +205,9 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
> }
> }
>
> + intel_dp_aux_set_backlight(conn_state,
> + connector->panel.backlight.level);
> set_aux_backlight_enable(intel_dp, true);
> - intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
> }
>
> static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)

--
Jani Nikula, Intel Open Source Graphics Center

2019-12-03 12:43:05

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH 4/5] drm/i915: Auto detect DPCD backlight support by default

On Fri, 22 Nov 2019, Lyude Paul <[email protected]> wrote:
> Turns out we actually already have some companies, such as Lenovo,
> shipping machines with AMOLED screens that don't allow controlling the
> backlight through the usual PWM interface and only allow controlling it
> through the standard EDP DPCD interface. One example of one of these
> laptops is the X1 Extreme 2nd Generation.
>
> Since we've got systems that need this turned on by default now to have
> backlight controls working out of the box, let's start auto-detecting it
> for systems by default based on what the VBT tells us. We do this by
> changing the default value for the enable_dpcd_backlight module param
> from 0 to -1.
>
> Signed-off-by: Lyude Paul <[email protected]>

Reviewed-by: Jani Nikula <[email protected]>

> ---
> drivers/gpu/drm/i915/i915_params.c | 2 +-
> drivers/gpu/drm/i915/i915_params.h | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
> index 1dd1f3652795..31eed60c167e 100644
> --- a/drivers/gpu/drm/i915/i915_params.c
> +++ b/drivers/gpu/drm/i915/i915_params.c
> @@ -172,7 +172,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400,
>
> i915_param_named(enable_dpcd_backlight, int, 0600,
> "Enable support for DPCD backlight control"
> - "(-1=use per-VBT LFP backlight type setting, 0=disabled [default], 1=enabled)");
> + "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)");
>
> #if IS_ENABLED(CONFIG_DRM_I915_GVT)
> i915_param_named(enable_gvt, bool, 0400,
> diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
> index 31b88f297fbc..a79d0867f77a 100644
> --- a/drivers/gpu/drm/i915/i915_params.h
> +++ b/drivers/gpu/drm/i915/i915_params.h
> @@ -64,7 +64,7 @@ struct drm_printer;
> param(int, reset, 3) \
> param(unsigned int, inject_probe_failure, 0) \
> param(int, fastboot, -1) \
> - param(int, enable_dpcd_backlight, 0) \
> + param(int, enable_dpcd_backlight, -1) \
> param(char *, force_probe, CONFIG_DRM_I915_FORCE_PROBE) \
> param(unsigned long, fake_lmem_start, 0) \
> /* leave bools at the end to not create holes */ \

--
Jani Nikula, Intel Open Source Graphics Center

2019-12-03 12:43:12

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH 5/5] drm/i915: Force DPCD backlight mode on X1 Extreme 2nd Gen 4K AMOLED panel

On Fri, 22 Nov 2019, Lyude Paul <[email protected]> wrote:
> Annoyingly, the VBT on the ThinkPad X1 Extreme 2nd Gen indicates that
> the system uses plain PWM based backlight controls, when in reality the
> only backlight controls that work are the standard VESA eDP DPCD
> backlight controls.
>
> Honestly, this makes me wonder how many other systems have these issues
> or lie about this in their VBT. Not sure we have any good way of finding
> out until panels like this become more common place in the laptop
> market. For now, just add a DRM DP quirk to indicate that this panel is
> telling the truth and is being a good LCD.
>
> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=112376
> Signed-off-by: Lyude Paul <[email protected]>

Acked-by: Jani Nikula <[email protected]>

> ---
> drivers/gpu/drm/drm_dp_helper.c | 4 ++++
> drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 10 ++++++++--
> include/drm/drm_dp_helper.h | 8 ++++++++
> 3 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 2c7870aef469..ec7061e3a99b 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -1155,6 +1155,10 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
> { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
> /* CH7511 seems to leave SINK_COUNT zeroed */
> { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
> + /* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation
> + * only supports DPCD backlight controls, despite advertising otherwise
> + */
> + { OUI(0xba, 0x41, 0x59), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT) },
> };
>
> #undef OUI
> 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 87b59db9ffe3..3d61260b08ad 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -325,11 +325,17 @@ 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 drm_i915_private *dev_priv = to_i915(intel_connector->base.dev);
> + struct intel_dp *intel_dp =
> + enc_to_intel_dp(&intel_connector->encoder->base);
> + struct drm_i915_private *dev_priv =
> + to_i915(intel_connector->base.dev);
>
> if (i915_modparams.enable_dpcd_backlight == 0 ||
> (i915_modparams.enable_dpcd_backlight == -1 &&
> - dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE))
> + dev_priv->vbt.backlight.type !=
> + INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
> + !drm_dp_has_quirk(&intel_dp->desc,
> + DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT)))
> return -ENODEV;
>
> if (!intel_dp_aux_display_control_capable(intel_connector))
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index 51ecb5112ef8..a444209cd54b 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -1520,6 +1520,14 @@ enum drm_dp_quirk {
> * The driver should ignore SINK_COUNT during detection.
> */
> DP_DPCD_QUIRK_NO_SINK_COUNT,
> + /**
> + * @DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT:
> + *
> + * The device is telling the truth when it says that it uses DPCD
> + * backlight controls, even if the system's firmware disagrees.
> + * The driver should honor the DPCD backlight capabilities advertised.
> + */
> + DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT,
> };
>
> /**

--
Jani Nikula, Intel Open Source Graphics Center

2019-12-03 22:44:40

by Lyude Paul

[permalink] [raw]
Subject: [PATCH v2] drm/i915: Assume 100% brightness when not in DPCD control mode

Currently we always determine the initial panel brightness level by
simply reading the value from DP_EDP_BACKLIGHT_BRIGHTNESS_MSB/LSB. This
seems wrong though, because if the panel is not currently in DPCD
control mode there's not really any reason why there would be any
brightness value programmed in the first place.

This appears to be the case on the Lenovo ThinkPad X1 Extreme 2nd
Generation, where the default value in these registers is always 0 on
boot despite the fact the panel runs at max brightness by default.
Getting the initial brightness value correct here is important as well,
since the panel on this laptop doesn't behave well if it's ever put into
DPCD control mode while the brightness level is programmed to 0.

So, let's fix this by checking what the current backlight control mode
is before reading the brightness level. If it's in DPCD control mode, we
return the programmed brightness level. Otherwise we assume 100%
brightness and return the highest possible brightness level. This also
prevents us from accidentally programming a brightness level of 0.

This is one of the many fixes that gets backlight controls working on
the ThinkPad X1 Extreme 2nd Generation with optional 4K AMOLED screen.

Changes since v1:
* s/DP_EDP_DISPLAY_CONTROL_REGISTER/DP_EDP_BACKLIGHT_MODE_SET_REGISTER/
- Jani

Signed-off-by: Lyude Paul <[email protected]>
---
.../drm/i915/display/intel_dp_aux_backlight.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

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 fad470553cf9..4d467e7d29eb 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -59,8 +59,25 @@ static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
u8 read_val[2] = { 0x0 };
+ u8 mode_reg;
u16 level = 0;

+ if (drm_dp_dpcd_readb(&intel_dp->aux,
+ DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
+ &mode_reg) != 1) {
+ DRM_DEBUG_KMS("Failed to read the DPCD register 0x%x\n",
+ DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
+ return 0;
+ }
+
+ /*
+ * If we're not in DPCD control mode yet, the programmed brightness
+ * value is meaningless and we should assume max brightness
+ */
+ if ((mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) !=
+ DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD)
+ return connector->panel.backlight.max;
+
if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
&read_val, sizeof(read_val)) < 0) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
--
2.23.0

2019-12-20 10:08:03

by Perr Yuan

[permalink] [raw]
Subject: Re: [1/5] drm/i915: Fix eDP DPCD aux max backlight calculations

On 11/22/19 6:15 PM, Lyude Paul wrote:
> Max backlight value for the panel was being calculated using byte
> count i.e. 0xffff if 2 bytes are supported for backlight brightness
> and 0xff if 1 byte is supported. However, EDP_PWMGEN_BIT_COUNT
> determines the number of active control bits used for the brightness
> setting. Thus, even if the panel uses 2 byte setting, it might not use
> all the control bits. Thus, max backlight should be set based on the
> value of EDP_PWMGEN_BIT_COUNT instead of assuming 65535 or 255.
>
> Additionally, EDP_PWMGEN_BIT_COUNT was being updated based on the VBT
> frequency which results in a different max backlight value. Thus,
> setting of EDP_PWMGEN_BIT_COUNT is moved to setup phase instead of
> enable so that max backlight can be calculated correctly. Only the
> frequency divider is set during the enable phase using the value of
> EDP_PWMGEN_BIT_COUNT.
>
> This is based off the original patch series from Furquan Shaikh
> <[email protected]>:
>
> https://patchwork.freedesktop.org/patch/317255/?series=62326&rev=3
>
> Changes since original patch:
> * Remove unused intel_dp variable in intel_dp_aux_setup_backlight()
> * Fix checkpatch issues
> * Make sure that we rewrite the pwmgen bit count whenever we bring the
> panel out of D3 mode
>
> Cc: Furquan Shaikh <[email protected]>
> Signed-off-by: Lyude Paul <[email protected]>
> ---
> .../drm/i915/display/intel_display_types.h | 3 +
> .../drm/i915/display/intel_dp_aux_backlight.c | 139 ++++++++++++------
> 2 files changed, 95 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
> index 83ea04149b77..2a8d8cae638e 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
> @@ -214,6 +214,9 @@ struct intel_panel {
> u8 controller; /* bxt+ only */
> struct pwm_device *pwm;
>
> + /* DPCD backlight */
> + u8 pwmgen_bit_count;
> +
> struct backlight_device *device;
>
> /* Connector and platform specific backlight functions */
> 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..fad470553cf9 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -111,61 +111,28 @@ static bool intel_dp_aux_set_pwm_freq(struct intel_connector *connector)
> {
> struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
> struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> - int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
> - u8 pn, pn_min, pn_max;
> + const u8 pn = connector->panel.backlight.pwmgen_bit_count;
> + int freq, fxp, f, fxp_actual, fxp_min, fxp_max;
>
> - /* Find desired value of (F x P)
> - * Note that, if F x P is out of supported range, the maximum value or
> - * minimum value will applied automatically. So no need to check that.
> - */
> freq = dev_priv->vbt.backlight.pwm_freq_hz;
> - DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
> if (!freq) {
> DRM_DEBUG_KMS("Use panel default backlight frequency\n");
> return false;
> }
>
> fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
> + f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
> + fxp_actual = f << pn;
>
> - /* Use highest possible value of Pn for more granularity of brightness
> - * adjustment while satifying the conditions below.
> - * - Pn is in the range of Pn_min and Pn_max
> - * - F is in the range of 1 and 255
> - * - FxP is within 25% of desired value.
> - * Note: 25% is arbitrary value and may need some tweak.
> - */
> - if (drm_dp_dpcd_readb(&intel_dp->aux,
> - DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
> - DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
> - return false;
> - }
> - if (drm_dp_dpcd_readb(&intel_dp->aux,
> - DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
> - DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
> - return false;
> - }
> - pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
> - pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
> -
> + /* Ensure frequency is within 25% of desired value */
> fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
> fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
> - if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
> - DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
> - return false;
> - }
> -
> - for (pn = pn_max; pn >= pn_min; pn--) {
> - f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
> - fxp_actual = f << pn;
> - if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
> - break;
> - }
>
> - if (drm_dp_dpcd_writeb(&intel_dp->aux,
> - DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
> - DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
> + if (fxp_min > fxp_actual || fxp_actual > fxp_max) {
> + DRM_DEBUG_KMS("Actual frequency out of range\n");
> return false;
> }
> +
> if (drm_dp_dpcd_writeb(&intel_dp->aux,
> DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
> DRM_DEBUG_KMS("Failed to write aux backlight freq\n");
> @@ -178,6 +145,7 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
> const struct drm_connector_state *conn_state)
> {
> struct intel_connector *connector = to_intel_connector(conn_state->connector);
> + struct intel_panel *panel = &connector->panel;
> struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
>
> @@ -197,6 +165,12 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
> case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
> new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
> new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
> +
> + if (drm_dp_dpcd_writeb(&intel_dp->aux,
> + DP_EDP_PWMGEN_BIT_COUNT,
> + panel->backlight.pwmgen_bit_count) < 0)
> + DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
> +
> break;
>
> /* Do nothing when it is already DPCD mode */
> @@ -225,20 +199,91 @@ 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 u32 intel_dp_aux_calc_max_backlight(struct intel_connector *connector)
> +{
> + struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
> + struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> + struct intel_panel *panel = &connector->panel;
> + u32 max_backlight = 0;
> + int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
> + u8 pn, pn_min, pn_max;
> +
> + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn)) {
> + pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
> + max_backlight = (1 << pn) - 1;
> + }
> +
> + /* Find desired value of (F x P)
> + * Note that, if F x P is out of supported range, the maximum value or
> + * minimum value will applied automatically. So no need to check that.
> + */
> + freq = dev_priv->vbt.backlight.pwm_freq_hz;
> + DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
> + if (!freq) {
> + DRM_DEBUG_KMS("Use panel default backlight frequency\n");
> + return max_backlight;
> + }
> +
> + fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
> +
> + /* Use highest possible value of Pn for more granularity of brightness
> + * adjustment while satifying the conditions below.
> + * - Pn is in the range of Pn_min and Pn_max
> + * - F is in the range of 1 and 255
> + * - FxP is within 25% of desired value.
> + * Note: 25% is arbitrary value and may need some tweak.
> + */
> + if (drm_dp_dpcd_readb(&intel_dp->aux,
> + DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
> + DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
> + return max_backlight;
> + }
> + if (drm_dp_dpcd_readb(&intel_dp->aux,
> + DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
> + DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
> + return max_backlight;
> + }
> + pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
> + pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
> +
> + fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
> + fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
> + if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
> + DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
> + return max_backlight;
> + }
> +
> + for (pn = pn_max; pn >= pn_min; pn--) {
> + f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
> + fxp_actual = f << pn;
> + if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
> + break;
> + }
> +
> + DRM_DEBUG_KMS("Using eDP pwmgen bit count of %d\n", pn);
> + if (drm_dp_dpcd_writeb(&intel_dp->aux,
> + DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
> + DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
> + return max_backlight;
> + }
> + panel->backlight.pwmgen_bit_count = pn;
> +
> + max_backlight = (1 << pn) - 1;
> +
> + return max_backlight;
> +}
> +
> static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
> enum pipe pipe)
> {
> - struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> struct intel_panel *panel = &connector->panel;
>
> - if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
> - panel->backlight.max = 0xFFFF;
> - else
> - panel->backlight.max = 0xFF;
> + panel->backlight.max = intel_dp_aux_calc_max_backlight(connector);
> + if (!panel->backlight.max)
> + return -ENODEV;
>
> panel->backlight.min = 0;
> panel->backlight.level = intel_dp_aux_get_backlight(connector);
> -
> panel->backlight.enabled = panel->backlight.level != 0;
>
> return 0;
>
> From patchwork Tue Dec 3 22:42:35 2019
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [v2] drm/i915: Assume 100% brightness when not in DPCD control mode
> From: Lyude Paul <[email protected]>
> X-Patchwork-Id: 343592
> Message-Id: <[email protected]>
> To: [email protected]
> Cc: David Airlie <[email protected]>, [email protected],
> [email protected]
> Date: Tue, 3 Dec 2019 17:42:35 -0500
>
> Currently we always determine the initial panel brightness level by
> simply reading the value from DP_EDP_BACKLIGHT_BRIGHTNESS_MSB/LSB. This
> seems wrong though, because if the panel is not currently in DPCD
> control mode there's not really any reason why there would be any
> brightness value programmed in the first place.
>
> This appears to be the case on the Lenovo ThinkPad X1 Extreme 2nd
> Generation, where the default value in these registers is always 0 on
> boot despite the fact the panel runs at max brightness by default.
> Getting the initial brightness value correct here is important as well,
> since the panel on this laptop doesn't behave well if it's ever put into
> DPCD control mode while the brightness level is programmed to 0.
>
> So, let's fix this by checking what the current backlight control mode
> is before reading the brightness level. If it's in DPCD control mode, we
> return the programmed brightness level. Otherwise we assume 100%
> brightness and return the highest possible brightness level. This also
> prevents us from accidentally programming a brightness level of 0.
>
> This is one of the many fixes that gets backlight controls working on
> the ThinkPad X1 Extreme 2nd Generation with optional 4K AMOLED screen.
>
> Changes since v1:
> * s/DP_EDP_DISPLAY_CONTROL_REGISTER/DP_EDP_BACKLIGHT_MODE_SET_REGISTER/
> - Jani
>
> Signed-off-by: Lyude Paul <[email protected]>
> ---
> .../drm/i915/display/intel_dp_aux_backlight.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> 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 fad470553cf9..4d467e7d29eb 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -59,8 +59,25 @@ static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
> {
> struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> u8 read_val[2] = { 0x0 };
> + u8 mode_reg;
> u16 level = 0;
>
> + if (drm_dp_dpcd_readb(&intel_dp->aux,
> + DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
> + &mode_reg) != 1) {
> + DRM_DEBUG_KMS("Failed to read the DPCD register 0x%x\n",
> + DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
> + return 0;
> + }
> +
> + /*
> + * If we're not in DPCD control mode yet, the programmed brightness
> + * value is meaningless and we should assume max brightness
> + */
> + if ((mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) !=
> + DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD)
> + return connector->panel.backlight.max;
> +
> if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
> &read_val, sizeof(read_val)) < 0) {
> DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
>
> From patchwork Fri Nov 22 23:16:01 2019
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [3/5] drm/i915: Fix DPCD register order in intel_dp_aux_enable_backlight()
> From: Lyude Paul <[email protected]>
> X-Patchwork-Id: 342164
> Message-Id: <[email protected]>
> To: [email protected]
> Cc: David Airlie <[email protected]>, [email protected],
> [email protected]
> Date: Fri, 22 Nov 2019 18:16:01 -0500
>
> For eDP panels, it appears it's expected that so long as the panel is in
> DPCD control mode that the brightness value is never set to 0. Instead,
> if the desired effect is to set the panel's backlight to 0 we're
> expected to simply turn off the backlight through the
> DP_EDP_DISPLAY_CONTROL_REGISTER.
>
> We already do the latter correctly in intel_dp_aux_disable_backlight().
> But, we make the mistake of writing the DPCD registers in the wrong
> order when enabling the backlight in intel_dp_aux_enable_backlight()
> since we currently enable the backlight through
> DP_EDP_DISPLAY_CONTROL_REGISTER before writing the brightness level. On
> the X1 Extreme 2nd Generation, this appears to have the potential of
> confusing the panel in such a way that further attempts to set the
> brightness don't actually change the backlight as expected and leave it
> off. Presumably, this happens because the incorrect register writing
> order briefly leaves the panel with DPCD mode enabled and a 0 brightness
> level set.
>
> So, reverse the order we write the DPCD registers when enabling the
> panel backlight so that we write the brightness value first, and enable
> the backlight second. This fix appears to be the final bit needed to get
> the backlight on the ThinkPad X1 Extreme 2nd Generation's AMOLED screen
> working.
>
> Signed-off-by: Lyude Paul <[email protected]>
> Reviewed-by: Jani Nikula <[email protected]>
> ---
> drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> 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 0bf8772bc7bb..87b59db9ffe3 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -205,8 +205,9 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
> }
> }
>
> + intel_dp_aux_set_backlight(conn_state,
> + connector->panel.backlight.level);
> set_aux_backlight_enable(intel_dp, true);
> - intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
> }
>
> static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
>
> From patchwork Fri Nov 22 23:16:02 2019
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [4/5] drm/i915: Auto detect DPCD backlight support by default
> From: Lyude Paul <[email protected]>
> X-Patchwork-Id: 342165
> Message-Id: <[email protected]>
> To: [email protected]
> Cc: David Airlie <[email protected]>, [email protected],
> [email protected]
> Date: Fri, 22 Nov 2019 18:16:02 -0500
>
> Turns out we actually already have some companies, such as Lenovo,
> shipping machines with AMOLED screens that don't allow controlling the
> backlight through the usual PWM interface and only allow controlling it
> through the standard EDP DPCD interface. One example of one of these
> laptops is the X1 Extreme 2nd Generation.
>
> Since we've got systems that need this turned on by default now to have
> backlight controls working out of the box, let's start auto-detecting it
> for systems by default based on what the VBT tells us. We do this by
> changing the default value for the enable_dpcd_backlight module param
> from 0 to -1.
>
> Signed-off-by: Lyude Paul <[email protected]>
> Reviewed-by: Jani Nikula <[email protected]>
> ---
> drivers/gpu/drm/i915/i915_params.c | 2 +-
> drivers/gpu/drm/i915/i915_params.h | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
> index 1dd1f3652795..31eed60c167e 100644
> --- a/drivers/gpu/drm/i915/i915_params.c
> +++ b/drivers/gpu/drm/i915/i915_params.c
> @@ -172,7 +172,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400,
>
> i915_param_named(enable_dpcd_backlight, int, 0600,
> "Enable support for DPCD backlight control"
> - "(-1=use per-VBT LFP backlight type setting, 0=disabled [default], 1=enabled)");
> + "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)");
>
> #if IS_ENABLED(CONFIG_DRM_I915_GVT)
> i915_param_named(enable_gvt, bool, 0400,
> diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
> index 31b88f297fbc..a79d0867f77a 100644
> --- a/drivers/gpu/drm/i915/i915_params.h
> +++ b/drivers/gpu/drm/i915/i915_params.h
> @@ -64,7 +64,7 @@ struct drm_printer;
> param(int, reset, 3) \
> param(unsigned int, inject_probe_failure, 0) \
> param(int, fastboot, -1) \
> - param(int, enable_dpcd_backlight, 0) \
> + param(int, enable_dpcd_backlight, -1) \
> param(char *, force_probe, CONFIG_DRM_I915_FORCE_PROBE) \
> param(unsigned long, fake_lmem_start, 0) \
> /* leave bools at the end to not create holes */ \
>
> From patchwork Fri Nov 22 23:16:03 2019
> Content-Type: text/plain; charset="utf-8"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> Subject: [5/5] drm/i915: Force DPCD backlight mode on X1 Extreme 2nd Gen 4K AMOLED panel
> From: Lyude Paul <[email protected]>
> X-Patchwork-Id: 342166
> Message-Id: <[email protected]>
> To: [email protected]
> Cc: David Airlie <[email protected]>, [email protected],
> [email protected], Maxime Ripard <[email protected]>
> Date: Fri, 22 Nov 2019 18:16:03 -0500
>
> Annoyingly, the VBT on the ThinkPad X1 Extreme 2nd Gen indicates that
> the system uses plain PWM based backlight controls, when in reality the
> only backlight controls that work are the standard VESA eDP DPCD
> backlight controls.
>
> Honestly, this makes me wonder how many other systems have these issues
> or lie about this in their VBT. Not sure we have any good way of finding
> out until panels like this become more common place in the laptop
> market. For now, just add a DRM DP quirk to indicate that this panel is
> telling the truth and is being a good LCD.
>
> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=112376
> Signed-off-by: Lyude Paul <[email protected]>
> Acked-by: Jani Nikula <[email protected]>
> ---
> drivers/gpu/drm/drm_dp_helper.c | 4 ++++
> drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 10 ++++++++--
> include/drm/drm_dp_helper.h | 8 ++++++++
> 3 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 2c7870aef469..ec7061e3a99b 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -1155,6 +1155,10 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
> { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
> /* CH7511 seems to leave SINK_COUNT zeroed */
> { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
> + /* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation
> + * only supports DPCD backlight controls, despite advertising otherwise
> + */
> + { OUI(0xba, 0x41, 0x59), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT) },
> };
>
> #undef OUI
> 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 87b59db9ffe3..3d61260b08ad 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -325,11 +325,17 @@ 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 drm_i915_private *dev_priv = to_i915(intel_connector->base.dev);
> + struct intel_dp *intel_dp =
> + enc_to_intel_dp(&intel_connector->encoder->base);
> + struct drm_i915_private *dev_priv =
> + to_i915(intel_connector->base.dev);
>
> if (i915_modparams.enable_dpcd_backlight == 0 ||
> (i915_modparams.enable_dpcd_backlight == -1 &&
> - dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE))
> + dev_priv->vbt.backlight.type !=
> + INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
> + !drm_dp_has_quirk(&intel_dp->desc,
> + DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT)))
> return -ENODEV;
>
> if (!intel_dp_aux_display_control_capable(intel_connector))
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index 51ecb5112ef8..a444209cd54b 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -1520,6 +1520,14 @@ enum drm_dp_quirk {
> * The driver should ignore SINK_COUNT during detection.
> */
> DP_DPCD_QUIRK_NO_SINK_COUNT,
> + /**
> + * @DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT:
> + *
> + * The device is telling the truth when it says that it uses DPCD
> + * backlight controls, even if the system's firmware disagrees.
> + * The driver should honor the DPCD backlight capabilities advertised.
> + */
> + DP_DPCD_QUIRK_FORCE_DPCD_BACKLIGHT,
> };
>
> /**
>
Tested-by:Perry Yuan <[email protected]>

2019-12-23 07:19:17

by Perr Yuan

[permalink] [raw]
Subject: Re: [v2] drm/i915: Assume 100% brightness when not in DPCD control mode

On 12/3/19 5:42 PM, Lyude Paul wrote:
> Currently we always determine the initial panel brightness level by
> simply reading the value from DP_EDP_BACKLIGHT_BRIGHTNESS_MSB/LSB. This
> seems wrong though, because if the panel is not currently in DPCD
> control mode there's not really any reason why there would be any
> brightness value programmed in the first place.
>
> This appears to be the case on the Lenovo ThinkPad X1 Extreme 2nd
> Generation, where the default value in these registers is always 0 on
> boot despite the fact the panel runs at max brightness by default.
> Getting the initial brightness value correct here is important as well,
> since the panel on this laptop doesn't behave well if it's ever put into
> DPCD control mode while the brightness level is programmed to 0.
>
> So, let's fix this by checking what the current backlight control mode
> is before reading the brightness level. If it's in DPCD control mode, we
> return the programmed brightness level. Otherwise we assume 100%
> brightness and return the highest possible brightness level. This also
> prevents us from accidentally programming a brightness level of 0.
>
> This is one of the many fixes that gets backlight controls working on
> the ThinkPad X1 Extreme 2nd Generation with optional 4K AMOLED screen.
>
> Changes since v1:
> * s/DP_EDP_DISPLAY_CONTROL_REGISTER/DP_EDP_BACKLIGHT_MODE_SET_REGISTER/
> - Jani
>
> Signed-off-by: Lyude Paul <[email protected]>
> ---
> .../drm/i915/display/intel_dp_aux_backlight.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> 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 fad470553cf9..4d467e7d29eb 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -59,8 +59,25 @@ static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
> {
> struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
> u8 read_val[2] = { 0x0 };
> + u8 mode_reg;
> u16 level = 0;
>
> + if (drm_dp_dpcd_readb(&intel_dp->aux,
> + DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
> + &mode_reg) != 1) {
> + DRM_DEBUG_KMS("Failed to read the DPCD register 0x%x\n",
> + DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
> + return 0;
> + }
> +
> + /*
> + * If we're not in DPCD control mode yet, the programmed brightness
> + * value is meaningless and we should assume max brightness
> + */
> + if ((mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) !=
> + DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD)
> + return connector->panel.backlight.max;
> +
> if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
> &read_val, sizeof(read_val)) < 0) {
> DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
>

Tested-by:Perry Yuan <[email protected]>

2019-12-23 07:20:08

by Perr Yuan

[permalink] [raw]
Subject: Re: [3/5] drm/i915: Fix DPCD register order in intel_dp_aux_enable_backlight()

On 11/22/19 6:16 PM, Lyude Paul wrote:
> For eDP panels, it appears it's expected that so long as the panel is in
> DPCD control mode that the brightness value is never set to 0. Instead,
> if the desired effect is to set the panel's backlight to 0 we're
> expected to simply turn off the backlight through the
> DP_EDP_DISPLAY_CONTROL_REGISTER.
>
> We already do the latter correctly in intel_dp_aux_disable_backlight().
> But, we make the mistake of writing the DPCD registers in the wrong
> order when enabling the backlight in intel_dp_aux_enable_backlight()
> since we currently enable the backlight through
> DP_EDP_DISPLAY_CONTROL_REGISTER before writing the brightness level. On
> the X1 Extreme 2nd Generation, this appears to have the potential of
> confusing the panel in such a way that further attempts to set the
> brightness don't actually change the backlight as expected and leave it
> off. Presumably, this happens because the incorrect register writing
> order briefly leaves the panel with DPCD mode enabled and a 0 brightness
> level set.
>
> So, reverse the order we write the DPCD registers when enabling the
> panel backlight so that we write the brightness value first, and enable
> the backlight second. This fix appears to be the final bit needed to get
> the backlight on the ThinkPad X1 Extreme 2nd Generation's AMOLED screen
> working.
>
> Signed-off-by: Lyude Paul <[email protected]>
> Reviewed-by: Jani Nikula <[email protected]>
> ---
> drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> 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 0bf8772bc7bb..87b59db9ffe3 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
> @@ -205,8 +205,9 @@ static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_st
> }
> }
>
> + intel_dp_aux_set_backlight(conn_state,
> + connector->panel.backlight.level);
> set_aux_backlight_enable(intel_dp, true);
> - intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
> }
>
> static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
>

Tested-by:Perry Yuan <[email protected]>

2019-12-23 07:22:33

by Perr Yuan

[permalink] [raw]
Subject: Re: [4/5] drm/i915: Auto detect DPCD backlight support by default

On 11/22/19 6:16 PM, Lyude Paul wrote:
> Turns out we actually already have some companies, such as Lenovo,
> shipping machines with AMOLED screens that don't allow controlling the
> backlight through the usual PWM interface and only allow controlling it
> through the standard EDP DPCD interface. One example of one of these
> laptops is the X1 Extreme 2nd Generation.
>
> Since we've got systems that need this turned on by default now to have
> backlight controls working out of the box, let's start auto-detecting it
> for systems by default based on what the VBT tells us. We do this by
> changing the default value for the enable_dpcd_backlight module param
> from 0 to -1.
>
> Signed-off-by: Lyude Paul <[email protected]>
> Reviewed-by: Jani Nikula <[email protected]>
> ---
> drivers/gpu/drm/i915/i915_params.c | 2 +-
> drivers/gpu/drm/i915/i915_params.h | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
> index 1dd1f3652795..31eed60c167e 100644
> --- a/drivers/gpu/drm/i915/i915_params.c
> +++ b/drivers/gpu/drm/i915/i915_params.c
> @@ -172,7 +172,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400,
>
> i915_param_named(enable_dpcd_backlight, int, 0600,
> "Enable support for DPCD backlight control"
> - "(-1=use per-VBT LFP backlight type setting, 0=disabled [default], 1=enabled)");
> + "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)");
>
> #if IS_ENABLED(CONFIG_DRM_I915_GVT)
> i915_param_named(enable_gvt, bool, 0400,
> diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
> index 31b88f297fbc..a79d0867f77a 100644
> --- a/drivers/gpu/drm/i915/i915_params.h
> +++ b/drivers/gpu/drm/i915/i915_params.h
> @@ -64,7 +64,7 @@ struct drm_printer;
> param(int, reset, 3) \
> param(unsigned int, inject_probe_failure, 0) \
> param(int, fastboot, -1) \
> - param(int, enable_dpcd_backlight, 0) \
> + param(int, enable_dpcd_backlight, -1) \
> param(char *, force_probe, CONFIG_DRM_I915_FORCE_PROBE) \
> param(unsigned long, fake_lmem_start, 0) \
> /* leave bools at the end to not create holes */ \
>

Tested-by:Perry Yuan <[email protected]>