Skip to content

Commit 68a7c52

Browse files
obbardcsuperna9999
authored andcommitted
drm/dp: clamp PWM bit count to advertised MIN and MAX capabilities
According to the eDP specification (VESA Embedded DisplayPort Standard v1.4b, Section 3.3.10.2), if the value of DP_EDP_PWMGEN_BIT_COUNT is less than DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, the sink is required to use the MIN value as the effective PWM bit count. This commit updates the logic to clamp the reported DP_EDP_PWMGEN_BIT_COUNT to the range defined by _CAP_MIN and _CAP_MAX. As part of this change, the behavior is modified such that reading both _CAP_MIN and _CAP_MAX registers is now required to succeed, otherwise bl->max value could end up being not set although drm_edp_backlight_probe_max() returned success. This ensures correct handling of eDP panels that report a zero PWM bit count but still provide valid non-zero MIN and MAX capability values. Without this clamping, brightness values may be interpreted incorrectly, leading to a dim or non-functional backlight. For example, the Samsung ATNA40YK20 OLED panel used in the Lenovo ThinkPad T14s Gen6 (Snapdragon) reports a PWM bit count of 0, but supports AUX backlight control and declares a valid 11-bit range. Clamping ensures brightness scaling works as intended on such panels. Co-developed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org> Tested-by: Christopher Obbard <christopher.obbard@linaro.org> Reviewed-by: Christopher Obbard <christopher.obbard@linaro.org> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org> Link: https://lore.kernel.org/r/20250814-topic-x1e80100-t14s-oled-dp-brightness-v7-1-b3d7b4dfe8c5@linaro.org
1 parent 3684218 commit 68a7c52

1 file changed

Lines changed: 49 additions & 19 deletions

File tree

drivers/gpu/drm/display/drm_dp_helper.c

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/init.h>
3030
#include <linux/iopoll.h>
3131
#include <linux/kernel.h>
32+
#include <linux/minmax.h>
3233
#include <linux/module.h>
3334
#include <linux/sched.h>
3435
#include <linux/seq_file.h>
@@ -4136,22 +4137,61 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
41364137
{
41374138
int fxp, fxp_min, fxp_max, fxp_actual, f = 1;
41384139
int ret;
4139-
u8 pn, pn_min, pn_max;
4140+
u8 pn, pn_min, pn_max, bit_count;
41404141

41414142
if (!bl->aux_set)
41424143
return 0;
41434144

4144-
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
4145+
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &bit_count);
41454146
if (ret < 0) {
41464147
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n",
41474148
aux->name, ret);
41484149
return -ENODEV;
41494150
}
41504151

4151-
pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4152+
bit_count &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4153+
4154+
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
4155+
if (ret < 0) {
4156+
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
4157+
aux->name, ret);
4158+
return -ENODEV;
4159+
}
4160+
pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4161+
4162+
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
4163+
if (ret < 0) {
4164+
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
4165+
aux->name, ret);
4166+
return -ENODEV;
4167+
}
4168+
pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4169+
4170+
if (unlikely(pn_min > pn_max)) {
4171+
drm_dbg_kms(aux->drm_dev, "%s: Invalid pwmgen bit count cap min/max returned: %d %d\n",
4172+
aux->name, pn_min, pn_max);
4173+
return -EINVAL;
4174+
}
4175+
4176+
/*
4177+
* Per VESA eDP Spec v1.4b, section 3.3.10.2:
4178+
* If DP_EDP_PWMGEN_BIT_COUNT is less than DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN,
4179+
* the sink must use the MIN value as the effective PWM bit count.
4180+
* Clamp the reported value to the [MIN, MAX] capability range to ensure
4181+
* correct brightness scaling on compliant eDP panels.
4182+
* Only enable this logic if the [MIN, MAX] range is valid in regard to Spec.
4183+
*/
4184+
pn = bit_count;
4185+
if (bit_count < pn_min)
4186+
pn = clamp(bit_count, pn_min, pn_max);
4187+
41524188
bl->max = (1 << pn) - 1;
4153-
if (!driver_pwm_freq_hz)
4189+
if (!driver_pwm_freq_hz) {
4190+
if (pn != bit_count)
4191+
goto bit_count_write_back;
4192+
41544193
return 0;
4194+
}
41554195

41564196
/*
41574197
* Set PWM Frequency divider to match desired frequency provided by the driver.
@@ -4175,21 +4215,6 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
41754215
* - FxP is within 25% of desired value.
41764216
* Note: 25% is arbitrary value and may need some tweak.
41774217
*/
4178-
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
4179-
if (ret < 0) {
4180-
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
4181-
aux->name, ret);
4182-
return 0;
4183-
}
4184-
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
4185-
if (ret < 0) {
4186-
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
4187-
aux->name, ret);
4188-
return 0;
4189-
}
4190-
pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4191-
pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
4192-
41934218
/* Ensure frequency is within 25% of desired value */
41944219
fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
41954220
fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
@@ -4207,12 +4232,17 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
42074232
break;
42084233
}
42094234

4235+
bit_count_write_back:
42104236
ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, pn);
42114237
if (ret < 0) {
42124238
drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
42134239
aux->name, ret);
42144240
return 0;
42154241
}
4242+
4243+
if (!driver_pwm_freq_hz)
4244+
return 0;
4245+
42164246
bl->pwmgen_bit_count = pn;
42174247
bl->max = (1 << pn) - 1;
42184248

0 commit comments

Comments
 (0)