[RFC v3 10/10] drm/dp: Extract i915's eDP backlight code into DRM helpers

Thomas Zimmermann tzimmermann at suse.de
Mon Feb 15 18:34:15 UTC 2021


Hi

Am 09.02.21 um 00:03 schrieb Lyude Paul:
>>
>>> +       } else {
>>> +               buf[0] = level;
>>> +       }
>>> +
>>> +       ret = drm_dp_dpcd_write(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf,
>>> sizeof(buf));
>>> +       if (ret != sizeof(buf)) {
>>> +               DRM_ERROR("%s: Failed to write aux backlight level: %d\n",
>>> aux->name, ret);
>>
>> Since you're adding this code, you should probably convert to drm_err()
>> helpers as well. Here and elsewhere.
> 
> this is next up on my todo list JFYI-I don't do it right now because there isn't
> actually any backpointer to the drm driver (and you can't just use the parent of
> the aux device, since that technically doesn't need to be the drm driver).
> 
> I'd add it in this series, but that's going to involve updating functions across
> the tree like drm_dp_aux_init() so I'd like to do it in a different patch series

Ok, sure. Makes sense.

Best regards
Thomas

> 
>>
>>> +               return ret < 0 ? ret : -EIO;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_edp_backlight_set_level);
>>> +
>>> +static int
>>> +drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl,
>>> +                            bool enable)
>>> +{
>>> +       int ret;
>>> +       u8 buf;
>>> +
>>> +       /* The panel uses something other then DPCD for enabling it's
>>> backlight */
>>
>> 'its'
>>
>> Best regards
>> Thomas
>>
>>> +       if (!bl->aux_enable)
>>> +               return 0;
>>> +
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, &buf);
>>> +       if (ret != 1) {
>>> +               DRM_ERROR("%s: Failed to read eDP display control register:
>>> %d\n", aux->name, ret);
>>> +               return ret < 0 ? ret : -EIO;
>>> +       }
>>> +       if (enable)
>>> +               buf |= DP_EDP_BACKLIGHT_ENABLE;
>>> +       else
>>> +               buf &= ~DP_EDP_BACKLIGHT_ENABLE;
>>> +
>>> +       ret = drm_dp_dpcd_writeb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, buf);
>>> +       if (ret != 1) {
>>> +               DRM_ERROR("%s: Failed to write eDP display control register:
>>> %d\n", aux->name, ret);
>>> +               return ret < 0 ? ret : -EIO;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * drm_edp_backlight_enable() - Enable an eDP panel's backlight using DPCD
>>> + * @aux: The DP AUX channel to use
>>> + * @bl: Backlight capability info from drm_edp_backlight_init()
>>> + * @level: The initial backlight level to set via AUX, if there is one
>>> + *
>>> + * This function handles enabling DPCD backlight controls on a panel over
>>> DPCD, while additionally
>>> + * restoring any important backlight state such as the given backlight
>>> level, the brightness byte
>>> + * count, backlight frequency, etc.
>>> + *
>>> + * Note that certain panels, while supporting brightness level controls
>>> over DPCD, may not support
>>> + * having their backlights enabled via the standard
>>> %DP_EDP_DISPLAY_CONTROL_REGISTER. On such panels
>>> + * &drm_edp_backlight_info.aux_enable will be set to %false, this function
>>> will skip the step of
>>> + * programming the %DP_EDP_DISPLAY_CONTROL_REGISTER, and the driver must
>>> perform the required
>>> + * implementation specific step for enabling the backlight after calling
>>> this function.
>>> + *
>>> + * Returns: %0 on success, negative error code on failure.
>>> + */
>>> +int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl,
>>> +                            const u16 level)
>>> +{
>>> +       int ret;
>>> +       u8 dpcd_buf, new_dpcd_buf;
>>> +
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
>>> &dpcd_buf);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to read backlight mode: %d\n",
>>> aux->name, ret);
>>> +               return ret < 0 ? ret : -EIO;
>>> +       }
>>> +
>>> +       new_dpcd_buf = dpcd_buf;
>>> +
>>> +       if ((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) !=
>>> DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
>>> +               new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
>>> +               new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
>>> +
>>> +               ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl-
>>>> pwmgen_bit_count);
>>> +               if (ret != 1)
>>> +                       DRM_DEBUG_KMS("%s: Failed to write aux pwmgen bit
>>> count: %d\n",
>>> +                                     aux->name, ret);
>>> +       }
>>> +
>>> +       if (bl->pwm_freq_pre_divider) {
>>> +               ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_FREQ_SET, bl-
>>>> pwm_freq_pre_divider);
>>> +               if (ret != 1)
>>> +                       DRM_DEBUG_KMS("%s: Failed to write aux backlight
>>> frequency: %d\n",
>>> +                                     aux->name, ret);
>>> +               else
>>> +                       new_dpcd_buf |=
>>> DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
>>> +       }
>>> +
>>> +       if (new_dpcd_buf != dpcd_buf) {
>>> +               ret = drm_dp_dpcd_writeb(aux,
>>> DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf);
>>> +               if (ret != 1) {
>>> +                       DRM_DEBUG_KMS("%s: Failed to write aux backlight
>>> mode: %d\n",
>>> +                                     aux->name, ret);
>>> +                       return ret < 0 ? ret : -EIO;
>>> +               }
>>> +       }
>>> +
>>> +       ret = drm_edp_backlight_set_level(aux, bl, level);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +       ret = drm_edp_backlight_set_enable(aux, bl, true);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_edp_backlight_enable);
>>> +
>>> +/**
>>> + * drm_edp_backlight_disable() - Disable an eDP backlight using DPCD, if
>>> supported
>>> + * @aux: The DP AUX channel to use
>>> + * @bl: Backlight capability info from drm_edp_backlight_init()
>>> + *
>>> + * This function handles disabling DPCD backlight controls on a panel over
>>> AUX. Note that some
>>> + * panels have backlights that are enabled/disabled by other means, despite
>>> having their brightness
>>> + * values controlled through DPCD. On such panels
>>> &drm_edp_backlight_info.aux_enable will be set to
>>> + * %false, this function will become a no-op (and we will skip updating
>>> + * %DP_EDP_DISPLAY_CONTROL_REGISTER), and the driver must take care to
>>> perform it's own
>>> + * implementation specific step for disabling the backlight.
>>> + *
>>> + * Returns: %0 on success or no-op, negative error code on failure.
>>> + */
>>> +int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl)
>>> +{
>>> +       int ret;
>>> +
>>> +       ret = drm_edp_backlight_set_enable(aux, bl, false);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_edp_backlight_disable);
>>> +
>>> +static inline int
>>> +drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct
>>> drm_edp_backlight_info *bl,
>>> +                           u16 driver_pwm_freq_hz, const u8
>>> edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
>>> +{
>>> +       int fxp, fxp_min, fxp_max, fxp_actual, f = 1;
>>> +       int ret;
>>> +       u8 pn, pn_min, pn_max;
>>> +
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to read pwmgen bit count cap:
>>> %d\n", aux->name, ret);
>>> +               return -ENODEV;
>>> +       }
>>> +
>>> +       pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
>>> +       bl->max = (1 << pn) - 1;
>>> +       if (!driver_pwm_freq_hz)
>>> +               return 0;
>>> +
>>> +       /*
>>> +        * Set PWM Frequency divider to match desired frequency provided by
>>> the driver.
>>> +        * The PWM Frequency is calculated as 27Mhz / (F x P).
>>> +        * - Where F = PWM Frequency Pre-Divider value programmed by field
>>> 7:0 of the
>>> +        *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
>>> +        * - Where P = 2^Pn, where Pn is the value programmed by field 4:0
>>> of the
>>> +        *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
>>> +        */
>>> +
>>> +       /* 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.
>>> +        */
>>> +       fxp = DIV_ROUND_CLOSEST(1000 * DP_EDP_BACKLIGHT_FREQ_BASE_KHZ,
>>> driver_pwm_freq_hz);
>>> +
>>> +       /* 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.
>>> +        */
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN,
>>> &pn_min);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to read pwmgen bit count cap min:
>>> %d\n", aux->name, ret);
>>> +               return 0;
>>> +       }
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX,
>>> &pn_max);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to read pwmgen bit count cap max:
>>> %d\n", aux->name, ret);
>>> +               return 0;
>>> +       }
>>> +       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("%s: Driver defined backlight frequency (%d)
>>> out of range\n",
>>> +                             aux->name, driver_pwm_freq_hz);
>>> +               return 0;
>>> +       }
>>> +
>>> +       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;
>>> +       }
>>> +
>>> +       ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, pn);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to write aux pwmgen bit count:
>>> %d\n", aux->name, ret);
>>> +               return 0;
>>> +       }
>>> +       bl->pwmgen_bit_count = pn;
>>> +       bl->max = (1 << pn) - 1;
>>> +
>>> +       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) {
>>> +               bl->pwm_freq_pre_divider = f;
>>> +               DRM_DEBUG_KMS("%s: Using backlight frequency from driver
>>> (%dHz)\n",
>>> +                             aux->name, driver_pwm_freq_hz);
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static inline int
>>> +drm_edp_backlight_probe_level(struct drm_dp_aux *aux, struct
>>> drm_edp_backlight_info *bl,
>>> +                             u8 *current_mode)
>>> +{
>>> +       int ret;
>>> +       u8 buf[2];
>>> +       u8 mode_reg;
>>> +
>>> +       ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
>>> &mode_reg);
>>> +       if (ret != 1) {
>>> +               DRM_DEBUG_KMS("%s: Failed to read backlight mode: %d\n",
>>> aux->name, ret);
>>> +               return ret < 0 ? ret : -EIO;
>>> +       }
>>> +
>>> +       *current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK);
>>> +       if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
>>> +               int size = 1 + bl->lsb_reg_used;
>>> +
>>> +               ret = drm_dp_dpcd_read(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
>>> buf, size);
>>> +               if (ret != size) {
>>> +                       DRM_DEBUG_KMS("%s: Failed to read backlight level:
>>> %d\n", aux->name, ret);
>>> +                       return ret < 0 ? ret : -EIO;
>>> +               }
>>> +
>>> +               if (bl->lsb_reg_used)
>>> +                       return (buf[0] << 8) | buf[1];
>>> +               else
>>> +                       return buf[0];
>>> +       }
>>> +
>>> +       /*
>>> +        * If we're not in DPCD control mode yet, the programmed brightness
>>> value is meaningless and
>>> +        * the driver should assume max brightness
>>> +        */
>>> +       return bl->max;
>>> +}
>>> +
>>> +/**
>>> + * drm_edp_backlight_init() - Probe a display panel's TCON using the
>>> standard VESA eDP backlight
>>> + * interface.
>>> + * @aux: The DP aux device to use for probing
>>> + * @bl: The &drm_edp_backlight_info struct to fill out with information on
>>> the backlight
>>> + * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz
>>> + * @edp_dpcd: A cached copy of the eDP DPCD
>>> + * @current_level: Where to store the probed brightness level
>>> + * @current_mode: Where to store the currently set backlight control mode
>>> + *
>>> + * Initializes a &drm_edp_backlight_info struct by probing @aux for it's
>>> backlight capabilities,
>>> + * along with also probing the current and maximum supported brightness
>>> levels.
>>> + *
>>> + * If @driver_pwm_freq_hz is non-zero, this will be used as the backlight
>>> frequency. Otherwise, the
>>> + * default frequency from the panel is used.
>>> + *
>>> + * Returns: %0 on success, negative error code on failure.
>>> + */
>>> +int
>>> +drm_edp_backlight_init(struct drm_dp_aux *aux, struct
>>> drm_edp_backlight_info *bl,
>>> +                      u16 driver_pwm_freq_hz, const u8
>>> edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
>>> +                      u16 *current_level, u8 *current_mode)
>>> +{
>>> +       int ret;
>>> +
>>> +       if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)
>>> +               bl->aux_enable = true;
>>> +       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
>>> +               bl->lsb_reg_used = true;
>>> +
>>> +       ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz,
>>> edp_dpcd);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       ret = drm_edp_backlight_probe_level(aux, bl, current_mode);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +       *current_level = ret;
>>> +
>>> +       DRM_DEBUG_KMS("%s: Found backlight level=%d/%d
>>> pwm_freq_pre_divider=%d mode=%x\n",
>>> +                     aux->name, *current_level, bl->max, bl-
>>>> pwm_freq_pre_divider, *current_mode);
>>> +       DRM_DEBUG_KMS("%s: Backlight caps: pwmgen_bit_count=%d
>>> lsb_reg_used=%d aux_enable=%d\n",
>>> +                     aux->name, bl->pwmgen_bit_count, bl->lsb_reg_used, bl-
>>>> aux_enable);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_edp_backlight_init);
>>> diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h
>>> b/drivers/gpu/drm/i915/display/intel_display_types.h
>>> index 16824eb3ef93..03051ab75d30 100644
>>> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
>>> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
>>> @@ -263,10 +263,7 @@ struct intel_panel {
>>>                  /* DPCD backlight */
>>>                  union {
>>>                          struct {
>>> -                               u8 pwmgen_bit_count;
>>> -                               u8 pwm_freq_pre_divider;
>>> -                               bool lsb_reg_used;
>>> -                               bool aux_enable;
>>> +                               struct drm_edp_backlight_info info;
>>>                          } vesa;
>>>                          struct {
>>>                                  bool sdr_uses_aux;
>>> 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 813f6c553156..286eb337448e 100644
>>> --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
>>> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
>>> @@ -270,114 +270,19 @@ intel_dp_aux_hdr_setup_backlight(struct
>>> intel_connector *connector, enum pipe pi
>>>    }
>>>    
>>>    /* VESA backlight callbacks */
>>> -static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector
>>> *connector)
>>> -{
>>> -       struct intel_dp *intel_dp = intel_attached_dp(connector);
>>> -       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>> -       int ret;
>>> -       u8 mode_reg;
>>> -
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read backlight mode:
>>> %d\n", ret);
>>> -               return false;
>>> -       }
>>> -
>>> -       return (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) ==
>>> -              DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
>>> -}
>>> -
>>> -/*
>>> - * Read the current backlight value from DPCD register(s) based
>>> - * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
>>> - */
>>>    static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector
>>> *connector, enum pipe unused)
>>>    {
>>> -       struct intel_dp *intel_dp = intel_attached_dp(connector);
>>> -       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>> -       int ret;
>>> -       u8 read_val[2] = { 0x0 };
>>> -       u16 level = 0;
>>> -
>>> -       /*
>>> -        * If we're not in DPCD control mode yet, the programmed brightness
>>> -        * value is meaningless and we should assume max brightness
>>> -        */
>>> -       if (!intel_dp_aux_vesa_backlight_dpcd_mode(connector))
>>> -               return connector->panel.backlight.max;
>>> -
>>> -       ret = drm_dp_dpcd_read(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, &read_val,
>>> -                              sizeof(read_val));
>>> -       if (ret != sizeof(read_val)) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read brightness level:
>>> %d\n", ret);
>>> -               return 0;
>>> -       }
>>> -
>>> -       if (connector->panel.backlight.edp.vesa.lsb_reg_used)
>>> -               level = (read_val[0] << 8 | read_val[1]);
>>> -       else
>>> -               level = read_val[0];
>>> -
>>> -       return level;
>>> +       return connector->panel.backlight.level;
>>>    }
>>>    
>>> -/*
>>> - * Sends the current backlight level over the aux channel, checking if its
>>> using
>>> - * 8-bit or 16 bit value (MSB and LSB)
>>> - */
>>>    static void
>>> -intel_dp_aux_vesa_set_backlight(const struct drm_connector_state
>>> *conn_state,
>>> -                               u32 level)
>>> +intel_dp_aux_vesa_set_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 = intel_attached_dp(connector);
>>> -       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>> -       int ret;
>>> -       u8 vals[2] = { 0x0 };
>>> -
>>> -       /* Write the MSB and/or LSB */
>>> -       if (connector->panel.backlight.edp.vesa.lsb_reg_used) {
>>> -               vals[0] = (level & 0xFF00) >> 8;
>>> -               vals[1] = (level & 0xFF);
>>> -       } else {
>>> -               vals[0] = level;
>>> -       }
>>> -
>>> -       ret = drm_dp_dpcd_write(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, vals,
>>> -                               sizeof(vals));
>>> -       if (ret != sizeof(vals)) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to write aux backlight
>>> level: %d\n", ret);
>>> -               return;
>>> -       }
>>> -}
>>> -
>>> -static void set_vesa_backlight_enable(struct intel_connector *connector,
>>> bool enable)
>>> -{
>>> -       struct intel_dp *intel_dp = intel_attached_dp(connector);
>>> -       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>> -       int ret;
>>> -       u8 reg_val = 0;
>>> -
>>> -       /* Early return when display use other mechanism to enable
>>> backlight. */
>>> -       if (!connector->panel.backlight.edp.vesa.aux_enable)
>>> -               return;
>>> -
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux,
>>> DP_EDP_DISPLAY_CONTROL_REGISTER, &reg_val);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read eDP display control
>>> register: %d\n", ret);
>>> -               return;
>>> -       }
>>> -
>>> -       if (enable)
>>> -               reg_val |= DP_EDP_BACKLIGHT_ENABLE;
>>> -       else
>>> -               reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
>>> +       struct intel_panel *panel = &connector->panel;
>>> +       struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
>>>    
>>> -       ret = drm_dp_dpcd_writeb(&intel_dp->aux,
>>> DP_EDP_DISPLAY_CONTROL_REGISTER, reg_val);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to %s aux backlight: %d\n",
>>> -                           enable ? "enable" : "disable", ret);
>>> -       }
>>> +       drm_edp_backlight_set_level(&intel_dp->aux, &panel-
>>>> backlight.edp.vesa.info, level);
>>>    }
>>>    
>>>    static void
>>> @@ -385,170 +290,46 @@ intel_dp_aux_vesa_enable_backlight(const struct
>>> intel_crtc_state *crtc_state,
>>>                                     const struct drm_connector_state
>>> *conn_state, u32 level)
>>>    {
>>>          struct intel_connector *connector = to_intel_connector(conn_state-
>>>> connector);
>>> -       struct intel_dp *intel_dp = intel_attached_dp(connector);
>>> -       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>>          struct intel_panel *panel = &connector->panel;
>>> -       int ret;
>>> -       u8 dpcd_buf, new_dpcd_buf;
>>> -       u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count;
>>> -
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read backlight mode:
>>> %d\n", ret);
>>> -               return;
>>> -       }
>>> -
>>> -       new_dpcd_buf = dpcd_buf;
>>> -
>>> -       if ((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) !=
>>> DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) {
>>> -               new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
>>> -               new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
>>> -
>>> -               ret = drm_dp_dpcd_writeb(&intel_dp->aux,
>>> DP_EDP_PWMGEN_BIT_COUNT, pwmgen_bit_count);
>>> -               if (ret != 1)
>>> -                       drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen
>>> bit count: %d\n", ret);
>>> -       }
>>> -
>>> -       if (panel->backlight.edp.vesa.pwm_freq_pre_divider) {
>>> -               ret = drm_dp_dpcd_writeb(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_FREQ_SET,
>>> -                                        panel-
>>>> backlight.edp.vesa.pwm_freq_pre_divider);
>>> -               if (ret == 1)
>>> -                       new_dpcd_buf |=
>>> DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
>>> -               else
>>> -                       drm_dbg_kms(&i915->drm, "Failed to write aux
>>> backlight frequency: %d\n",
>>> -                                   ret);
>>> -       }
>>> -
>>> -       if (new_dpcd_buf != dpcd_buf) {
>>> -               ret = drm_dp_dpcd_writeb(&intel_dp->aux,
>>> DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
>>> -                                        new_dpcd_buf);
>>> -               if (ret != 1)
>>> -                       drm_dbg_kms(&i915->drm, "Failed to write aux
>>> backlight mode: %d\n", ret);
>>> -       }
>>> +       struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
>>>    
>>> -       intel_dp_aux_vesa_set_backlight(conn_state, level);
>>> -       set_vesa_backlight_enable(connector, true);
>>> +       drm_edp_backlight_enable(&intel_dp->aux, &panel-
>>>> backlight.edp.vesa.info, level);
>>>    }
>>>    
>>>    static void intel_dp_aux_vesa_disable_backlight(const struct
>>> drm_connector_state *old_conn_state,
>>>                                                  u32 level)
>>>    {
>>> -       set_vesa_backlight_enable(to_intel_connector(old_conn_state-
>>>> connector), false);
>>> -}
>>> -
>>> -/*
>>> - * Compute PWM frequency divider value based off the frequency provided to
>>> us by the vbt.
>>> - * The PWM Frequency is calculated as 27Mhz / (F x P).
>>> - * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of
>>> the
>>> - *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
>>> - * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
>>> - *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
>>> - */
>>> -static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector
>>> *connector)
>>> -{
>>> -       struct drm_i915_private *i915 = to_i915(connector->base.dev);
>>> -       struct intel_dp *intel_dp = intel_attached_dp(connector);
>>> +       struct intel_connector *connector =
>>> to_intel_connector(old_conn_state->connector);
>>>          struct intel_panel *panel = &connector->panel;
>>> -       u32 max_backlight = 0;
>>> -       int ret, freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
>>> -       u8 pn, pn_min, pn_max;
>>> -
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT,
>>> &pn);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count
>>> cap: %d\n", ret);
>>> -               return 0;
>>> -       }
>>> -
>>> -       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 = i915->vbt.backlight.pwm_freq_hz;
>>> -       drm_dbg_kms(&i915->drm, "VBT defined backlight frequency %u Hz\n",
>>> -                   freq);
>>> -       if (!freq) {
>>> -               drm_dbg_kms(&i915->drm,
>>> -                           "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.
>>> -        */
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux,
>>> DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap
>>> min: %d\n", ret);
>>> -               return max_backlight;
>>> -       }
>>> -       ret = drm_dp_dpcd_readb(&intel_dp->aux,
>>> DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to read pwmgen bit count cap
>>> max: %d\n", ret);
>>> -               return max_backlight;
>>> -       }
>>> -       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_dbg_kms(&i915->drm,
>>> -                           "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_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn);
>>> -       ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT,
>>> pn);
>>> -       if (ret != 1) {
>>> -               drm_dbg_kms(&i915->drm, "Failed to write aux pwmgen bit
>>> count: %d\n", ret);
>>> -               return max_backlight;
>>> -       }
>>> -
>>> -       panel->backlight.edp.vesa.pwmgen_bit_count = pn;
>>> -       if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP)
>>> -               panel->backlight.edp.vesa.pwm_freq_pre_divider = f;
>>> -
>>> -       max_backlight = (1 << pn) - 1;
>>> +       struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
>>>    
>>> -       return max_backlight;
>>> +       drm_edp_backlight_disable(&intel_dp->aux, &panel-
>>>> backlight.edp.vesa.info);
>>>    }
>>>    
>>> -static int intel_dp_aux_vesa_setup_backlight(struct intel_connector
>>> *connector,
>>> -                                            enum pipe pipe)
>>> +static int intel_dp_aux_vesa_setup_backlight(struct intel_connector
>>> *connector, enum pipe pipe)
>>>    {
>>>          struct intel_dp *intel_dp = intel_attached_dp(connector);
>>>          struct intel_panel *panel = &connector->panel;
>>> +       struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>> +       u16 current_level;
>>> +       u8 current_mode;
>>> +       int ret;
>>>    
>>> -       if (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)
>>> -               panel->backlight.edp.vesa.aux_enable = true;
>>> -       if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
>>> -               panel->backlight.edp.vesa.lsb_reg_used = true;
>>> -
>>> -       panel->backlight.max =
>>> intel_dp_aux_vesa_calc_max_backlight(connector);
>>> -       if (!panel->backlight.max)
>>> -               return -ENODEV;
>>> +       ret = drm_edp_backlight_init(&intel_dp->aux, &panel-
>>>> backlight.edp.vesa.info,
>>> +                                    i915->vbt.backlight.pwm_freq_hz,
>>> intel_dp->edp_dpcd,
>>> +                                    &current_level, &current_mode);
>>> +       if (ret < 0)
>>> +               return ret;
>>>    
>>> +       panel->backlight.max = panel->backlight.edp.vesa.info.max;
>>>          panel->backlight.min = 0;
>>> -       panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector,
>>> pipe);
>>> -       panel->backlight.enabled =
>>> intel_dp_aux_vesa_backlight_dpcd_mode(connector) &&
>>> -                                  panel->backlight.level != 0;
>>> +       if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
>>> +               panel->backlight.level = current_level;
>>> +               panel->backlight.enabled = panel->backlight.level != 0;
>>> +       } else {
>>> +               panel->backlight.level = panel->backlight.max;
>>> +               panel->backlight.enabled = false;
>>> +       }
>>>    
>>>          return 0;
>>>    }
>>> @@ -559,16 +340,12 @@ intel_dp_aux_supports_vesa_backlight(struct
>>> intel_connector *connector)
>>>          struct intel_dp *intel_dp = intel_attached_dp(connector);
>>>          struct drm_i915_private *i915 = dp_to_i915(intel_dp);
>>>    
>>> -       /* Check the eDP Display control capabilities registers to determine
>>> if
>>> -        * the panel can support backlight control over the aux channel.
>>> -        *
>>> -        * TODO: We currently only support AUX only backlight
>>> configurations, not backlights which
>>> +       /* TODO: We currently only support AUX only backlight
>>> configurations, not backlights which
>>>           * require a mix of PWM and AUX controls to work. In the mean time,
>>> these machines typically
>>>           * work just fine using normal PWM controls anyway.
>>>           */
>>> -       if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
>>> -           (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
>>> -           (intel_dp->edp_dpcd[2] &
>>> DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {
>>> +       if ((intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
>>> +           drm_edp_backlight_supported(intel_dp->edp_dpcd)) {
>>>                  drm_dbg_kms(&i915->drm, "AUX Backlight Control
>>> Supported!\n");
>>>                  return true;
>>>          }
>>> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
>>> index edffd1dcca3e..1eca0b42fc45 100644
>>> --- a/include/drm/drm_dp_helper.h
>>> +++ b/include/drm/drm_dp_helper.h
>>> @@ -1790,6 +1790,24 @@ drm_dp_sink_can_do_video_without_timing_msa(const u8
>>> dpcd[DP_RECEIVER_CAP_SIZE])
>>>                  DP_MSA_TIMING_PAR_IGNORED;
>>>    }
>>>    
>>> +/**
>>> + * drm_edp_backlight_supported() - Check an eDP DPCD for VESA backlight
>>> support
>>> + * @edp_dpcd: The DPCD to check
>>> + *
>>> + * Note that currently this function will return %false for panels which
>>> support various DPCD
>>> + * backlight features but which require the brightness be set through PWM,
>>> and don't support setting
>>> + * the brightness level via the DPCD. This is a TODO.
>>> + *
>>> + * Returns: %True if @edp_dpcd indicates that VESA backlight controls are
>>> supported, %false
>>> + * otherwise
>>> + */
>>> +static inline bool
>>> +drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
>>> +{
>>> +       return (edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP) &&
>>> +               (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP);
>>> +}
>>> +
>>>    /*
>>>     * DisplayPort AUX channel
>>>     */
>>> @@ -2089,6 +2107,36 @@ drm_dp_has_quirk(const struct drm_dp_desc *desc, enum
>>> drm_dp_quirk quirk)
>>>          return desc->quirks & BIT(quirk);
>>>    }
>>>    
>>> +/**
>>> + * struct drm_edp_backlight_info - Probed eDP backlight info struct
>>> + * @pwmgen_bit_count: The pwmgen bit count
>>> + * @pwm_freq_pre_divider: The PWM frequency pre-divider value being used
>>> for this backlight, if any
>>> + * @max: The maximum backlight level that may be set
>>> + * @lsb_reg_used: Do we also write values to the
>>> DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register?
>>> + * @aux_enable: Does the panel support the AUX enable cap?
>>> + *
>>> + * This structure contains various data about an eDP backlight, which can
>>> be populated by using
>>> + * drm_edp_backlight_init().
>>> + */
>>> +struct drm_edp_backlight_info {
>>> +       u8 pwmgen_bit_count;
>>> +       u8 pwm_freq_pre_divider;
>>> +       u16 max;
>>> +
>>> +       bool lsb_reg_used : 1;
>>> +       bool aux_enable : 1;
>>> +};
>>> +
>>> +int
>>> +drm_edp_backlight_init(struct drm_dp_aux *aux, struct
>>> drm_edp_backlight_info *bl,
>>> +                      u16 driver_pwm_freq_hz, const u8
>>> edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
>>> +                      u16 *current_level, u8 *current_mode);
>>> +int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl,
>>> +                               u16 level);
>>> +int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl,
>>> +                            u16 level);
>>> +int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct
>>> drm_edp_backlight_info *bl);
>>> +
>>>    #ifdef CONFIG_DRM_DP_CEC
>>>    void drm_dp_cec_irq(struct drm_dp_aux *aux);
>>>    void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
>>>
>>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer

-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature
Type: application/pgp-signature
Size: 840 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/dri-devel/attachments/20210215/eaf99371/attachment-0001.sig>


More information about the dri-devel mailing list