Skip to content

Commit f58a435

Browse files
committed
drm/dp, drm/i915: Add support for VESA backlights using PWM for brightness control
Now that we've added support to i915 for controlling panel backlights that need PWM to be enabled/disabled, let's finalize this and add support for controlling brightness levels via PWM as well. This should hopefully put us towards the path of supporting _ALL_ backlights via VESA's DPCD interface which would allow us to finally start trusting the DPCD again. Note however that we still don't enable using this by default on i915 when it's not needed, primarily because I haven't yet had a chance to confirm if it's safe to do this on the one machine in Intel's CI that had an issue with this: samus-fi-bdw. I have done basic testing of this on other machines though, by manually patching i915 to force it into PWM-only mode on some of my laptops. v2: * Correct documentation (thanks Doug!) * Get rid of backlight caps Signed-off-by: Lyude Paul <[email protected]> Reviewed-by: Doug Anderson <[email protected]> Cc: Rajeev Nandan <[email protected]> Cc: Satadru Pramanik <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 6465964 commit f58a435

File tree

3 files changed

+89
-34
lines changed

3 files changed

+89
-34
lines changed

drivers/gpu/drm/drm_dp_helper.c

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,6 +3290,10 @@ int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_bac
32903290
int ret;
32913291
u8 buf[2] = { 0 };
32923292

3293+
/* The panel uses the PWM for controlling brightness levels */
3294+
if (!bl->aux_set)
3295+
return 0;
3296+
32933297
if (bl->lsb_reg_used) {
32943298
buf[0] = (level & 0xff00) >> 8;
32953299
buf[1] = (level & 0x00ff);
@@ -3316,7 +3320,7 @@ drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backli
33163320
int ret;
33173321
u8 buf;
33183322

3319-
/* The panel uses something other then DPCD for enabling its backlight */
3323+
/* This panel uses the EDP_BL_PWR GPIO for enablement */
33203324
if (!bl->aux_enable)
33213325
return 0;
33223326

@@ -3351,19 +3355,24 @@ drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backli
33513355
* restoring any important backlight state such as the given backlight level, the brightness byte
33523356
* count, backlight frequency, etc.
33533357
*
3354-
* Note that certain panels, while supporting brightness level controls over DPCD, may not support
3355-
* having their backlights enabled via the standard %DP_EDP_DISPLAY_CONTROL_REGISTER. On such panels
3356-
* &drm_edp_backlight_info.aux_enable will be set to %false, this function will skip the step of
3357-
* programming the %DP_EDP_DISPLAY_CONTROL_REGISTER, and the driver must perform the required
3358-
* implementation specific step for enabling the backlight after calling this function.
3358+
* Note that certain panels do not support being enabled or disabled via DPCD, but instead require
3359+
* that the driver handle enabling/disabling the panel through implementation-specific means using
3360+
* the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
3361+
* this function becomes a no-op, and the driver is expected to handle powering the panel on using
3362+
* the EDP_BL_PWR GPIO.
33593363
*
33603364
* Returns: %0 on success, negative error code on failure.
33613365
*/
33623366
int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
33633367
const u16 level)
33643368
{
33653369
int ret;
3366-
u8 dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
3370+
u8 dpcd_buf;
3371+
3372+
if (bl->aux_set)
3373+
dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
3374+
else
3375+
dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM;
33673376

33683377
if (bl->pwmgen_bit_count) {
33693378
ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count);
@@ -3405,12 +3414,13 @@ EXPORT_SYMBOL(drm_edp_backlight_enable);
34053414
* @aux: The DP AUX channel to use
34063415
* @bl: Backlight capability info from drm_edp_backlight_init()
34073416
*
3408-
* This function handles disabling DPCD backlight controls on a panel over AUX. Note that some
3409-
* panels have backlights that are enabled/disabled by other means, despite having their brightness
3410-
* values controlled through DPCD. On such panels &drm_edp_backlight_info.aux_enable will be set to
3411-
* %false, this function will become a no-op (and we will skip updating
3412-
* %DP_EDP_DISPLAY_CONTROL_REGISTER), and the driver must take care to perform it's own
3413-
* implementation specific step for disabling the backlight.
3417+
* This function handles disabling DPCD backlight controls on a panel over AUX.
3418+
*
3419+
* Note that certain panels do not support being enabled or disabled via DPCD, but instead require
3420+
* that the driver handle enabling/disabling the panel through implementation-specific means using
3421+
* the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
3422+
* this function becomes a no-op, and the driver is expected to handle powering the panel off using
3423+
* the EDP_BL_PWR GPIO.
34143424
*
34153425
* Returns: %0 on success or no-op, negative error code on failure.
34163426
*/
@@ -3434,6 +3444,9 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
34343444
int ret;
34353445
u8 pn, pn_min, pn_max;
34363446

3447+
if (!bl->aux_set)
3448+
return 0;
3449+
34373450
ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
34383451
if (ret != 1) {
34393452
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n",
@@ -3519,7 +3532,7 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
35193532
}
35203533

35213534
static inline int
3522-
drm_edp_backlight_probe_level(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
3535+
drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
35233536
u8 *current_mode)
35243537
{
35253538
int ret;
@@ -3534,6 +3547,9 @@ drm_edp_backlight_probe_level(struct drm_dp_aux *aux, struct drm_edp_backlight_i
35343547
}
35353548

35363549
*current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK);
3550+
if (!bl->aux_set)
3551+
return 0;
3552+
35373553
if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
35383554
int size = 1 + bl->lsb_reg_used;
35393555

@@ -3564,7 +3580,7 @@ drm_edp_backlight_probe_level(struct drm_dp_aux *aux, struct drm_edp_backlight_i
35643580
* @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight
35653581
* @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz
35663582
* @edp_dpcd: A cached copy of the eDP DPCD
3567-
* @current_level: Where to store the probed brightness level
3583+
* @current_level: Where to store the probed brightness level, if any
35683584
* @current_mode: Where to store the currently set backlight control mode
35693585
*
35703586
* Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities,
@@ -3584,24 +3600,38 @@ drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl
35843600

35853601
if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)
35863602
bl->aux_enable = true;
3603+
if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)
3604+
bl->aux_set = true;
35873605
if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
35883606
bl->lsb_reg_used = true;
35893607

3608+
/* Sanity check caps */
3609+
if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
3610+
drm_dbg_kms(aux->drm_dev,
3611+
"%s: Panel supports neither AUX or PWM brightness control? Aborting\n",
3612+
aux->name);
3613+
return -EINVAL;
3614+
}
3615+
35903616
ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd);
35913617
if (ret < 0)
35923618
return ret;
35933619

3594-
ret = drm_edp_backlight_probe_level(aux, bl, current_mode);
3620+
ret = drm_edp_backlight_probe_state(aux, bl, current_mode);
35953621
if (ret < 0)
35963622
return ret;
35973623
*current_level = ret;
35983624

35993625
drm_dbg_kms(aux->drm_dev,
3600-
"%s: Found backlight level=%d/%d pwm_freq_pre_divider=%d mode=%x\n",
3601-
aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider, *current_mode);
3602-
drm_dbg_kms(aux->drm_dev,
3603-
"%s: Backlight caps: pwmgen_bit_count=%d lsb_reg_used=%d aux_enable=%d\n",
3604-
aux->name, bl->pwmgen_bit_count, bl->lsb_reg_used, bl->aux_enable);
3626+
"%s: Found backlight: aux_set=%d aux_enable=%d mode=%d\n",
3627+
aux->name, bl->aux_set, bl->aux_enable, *current_mode);
3628+
if (bl->aux_set) {
3629+
drm_dbg_kms(aux->drm_dev,
3630+
"%s: Backlight caps: level=%d/%d pwm_freq_pre_divider=%d lsb_reg_used=%d\n",
3631+
aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider,
3632+
bl->lsb_reg_used);
3633+
}
3634+
36053635
return 0;
36063636
}
36073637
EXPORT_SYMBOL(drm_edp_backlight_init);

drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u3
282282
struct intel_panel *panel = &connector->panel;
283283
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
284284

285+
if (!panel->backlight.edp.vesa.info.aux_set) {
286+
const u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
287+
288+
intel_backlight_set_pwm_level(conn_state, pwm_level);
289+
}
290+
285291
drm_edp_backlight_set_level(&intel_dp->aux, &panel->backlight.edp.vesa.info, level);
286292
}
287293

@@ -294,8 +300,13 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
294300
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
295301

296302
if (!panel->backlight.edp.vesa.info.aux_enable) {
297-
u32 pwm_level = intel_backlight_invert_pwm_level(connector,
298-
panel->backlight.pwm_level_max);
303+
u32 pwm_level;
304+
305+
if (!panel->backlight.edp.vesa.info.aux_set)
306+
pwm_level = intel_backlight_level_to_pwm(connector, level);
307+
else
308+
pwm_level = intel_backlight_invert_pwm_level(connector,
309+
panel->backlight.pwm_level_max);
299310

300311
panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
301312
}
@@ -332,7 +343,7 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
332343
if (ret < 0)
333344
return ret;
334345

335-
if (!panel->backlight.edp.vesa.info.aux_enable) {
346+
if (!panel->backlight.edp.vesa.info.aux_set || !panel->backlight.edp.vesa.info.aux_enable) {
336347
ret = panel->backlight.pwm_funcs->setup(connector, pipe);
337348
if (ret < 0) {
338349
drm_err(&i915->drm,
@@ -341,14 +352,27 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
341352
return ret;
342353
}
343354
}
344-
panel->backlight.max = panel->backlight.edp.vesa.info.max;
345-
panel->backlight.min = 0;
346-
if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
347-
panel->backlight.level = current_level;
348-
panel->backlight.enabled = panel->backlight.level != 0;
355+
356+
if (panel->backlight.edp.vesa.info.aux_set) {
357+
panel->backlight.max = panel->backlight.edp.vesa.info.max;
358+
panel->backlight.min = 0;
359+
if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
360+
panel->backlight.level = current_level;
361+
panel->backlight.enabled = panel->backlight.level != 0;
362+
} else {
363+
panel->backlight.level = panel->backlight.max;
364+
panel->backlight.enabled = false;
365+
}
349366
} else {
350-
panel->backlight.level = panel->backlight.max;
351-
panel->backlight.enabled = false;
367+
panel->backlight.max = panel->backlight.pwm_level_max;
368+
panel->backlight.min = panel->backlight.pwm_level_min;
369+
if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_PWM) {
370+
panel->backlight.level = panel->backlight.pwm_funcs->get(connector, pipe);
371+
panel->backlight.enabled = panel->backlight.pwm_enabled;
372+
} else {
373+
panel->backlight.level = panel->backlight.max;
374+
panel->backlight.enabled = false;
375+
}
352376
}
353377

354378
return 0;

include/drm/drm_dp_helper.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,16 +1868,15 @@ drm_dp_sink_can_do_video_without_timing_msa(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
18681868
*
18691869
* Note that currently this function will return %false for panels which support various DPCD
18701870
* backlight features but which require the brightness be set through PWM, and don't support setting
1871-
* the brightness level via the DPCD. This is a TODO.
1871+
* the brightness level via the DPCD.
18721872
*
18731873
* Returns: %True if @edp_dpcd indicates that VESA backlight controls are supported, %false
18741874
* otherwise
18751875
*/
18761876
static inline bool
18771877
drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
18781878
{
1879-
return (edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP) &&
1880-
(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP);
1879+
return !!(edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP);
18811880
}
18821881

18831882
/*
@@ -2238,6 +2237,7 @@ drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
22382237
* @max: The maximum backlight level that may be set
22392238
* @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register?
22402239
* @aux_enable: Does the panel support the AUX enable cap?
2240+
* @aux_set: Does the panel support setting the brightness through AUX?
22412241
*
22422242
* This structure contains various data about an eDP backlight, which can be populated by using
22432243
* drm_edp_backlight_init().
@@ -2249,6 +2249,7 @@ struct drm_edp_backlight_info {
22492249

22502250
bool lsb_reg_used : 1;
22512251
bool aux_enable : 1;
2252+
bool aux_set : 1;
22522253
};
22532254

22542255
int

0 commit comments

Comments
 (0)