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

From: Thomas Zimmermann
Date: Mon Feb 15 2021 - 13:35:11 EST


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

Attachment: OpenPGP_signature
Description: OpenPGP digital signature