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

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_aux_backlight_enable
  2. intel_dp_aux_get_backlight
  3. intel_dp_aux_set_backlight
  4. intel_dp_aux_set_pwm_freq
  5. intel_dp_aux_enable_backlight
  6. intel_dp_aux_disable_backlight
  7. intel_dp_aux_setup_backlight
  8. intel_dp_aux_display_control_capable
  9. intel_dp_aux_init_backlight_funcs

   1 /*
   2  * Copyright © 2015 Intel Corporation
   3  *
   4  * Permission is hereby granted, free of charge, to any person obtaining a
   5  * copy of this software and associated documentation files (the "Software"),
   6  * to deal in the Software without restriction, including without limitation
   7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8  * and/or sell copies of the Software, and to permit persons to whom the
   9  * Software is furnished to do so, subject to the following conditions:
  10  *
  11  * The above copyright notice and this permission notice (including the next
  12  * paragraph) shall be included in all copies or substantial portions of the
  13  * Software.
  14  *
  15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21  * IN THE SOFTWARE.
  22  *
  23  */
  24 
  25 #include "intel_display_types.h"
  26 #include "intel_dp_aux_backlight.h"
  27 
  28 static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
  29 {
  30         u8 reg_val = 0;
  31 
  32         /* Early return when display use other mechanism to enable backlight. */
  33         if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
  34                 return;
  35 
  36         if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
  37                               &reg_val) < 0) {
  38                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
  39                               DP_EDP_DISPLAY_CONTROL_REGISTER);
  40                 return;
  41         }
  42         if (enable)
  43                 reg_val |= DP_EDP_BACKLIGHT_ENABLE;
  44         else
  45                 reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
  46 
  47         if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
  48                                reg_val) != 1) {
  49                 DRM_DEBUG_KMS("Failed to %s aux backlight\n",
  50                               enable ? "enable" : "disable");
  51         }
  52 }
  53 
  54 /*
  55  * Read the current backlight value from DPCD register(s) based
  56  * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
  57  */
  58 static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
  59 {
  60         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
  61         u8 read_val[2] = { 0x0 };
  62         u16 level = 0;
  63 
  64         if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
  65                              &read_val, sizeof(read_val)) < 0) {
  66                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
  67                               DP_EDP_BACKLIGHT_BRIGHTNESS_MSB);
  68                 return 0;
  69         }
  70         level = read_val[0];
  71         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
  72                 level = (read_val[0] << 8 | read_val[1]);
  73 
  74         return level;
  75 }
  76 
  77 /*
  78  * Sends the current backlight level over the aux channel, checking if its using
  79  * 8-bit or 16 bit value (MSB and LSB)
  80  */
  81 static void
  82 intel_dp_aux_set_backlight(const struct drm_connector_state *conn_state, u32 level)
  83 {
  84         struct intel_connector *connector = to_intel_connector(conn_state->connector);
  85         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
  86         u8 vals[2] = { 0x0 };
  87 
  88         vals[0] = level;
  89 
  90         /* Write the MSB and/or LSB */
  91         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) {
  92                 vals[0] = (level & 0xFF00) >> 8;
  93                 vals[1] = (level & 0xFF);
  94         }
  95         if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
  96                               vals, sizeof(vals)) < 0) {
  97                 DRM_DEBUG_KMS("Failed to write aux backlight level\n");
  98                 return;
  99         }
 100 }
 101 
 102 /*
 103  * Set PWM Frequency divider to match desired frequency in vbt.
 104  * The PWM Frequency is calculated as 27Mhz / (F x P).
 105  * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
 106  *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
 107  * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
 108  *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
 109  */
 110 static bool intel_dp_aux_set_pwm_freq(struct intel_connector *connector)
 111 {
 112         struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 113         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
 114         int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
 115         u8 pn, pn_min, pn_max;
 116 
 117         /* Find desired value of (F x P)
 118          * Note that, if F x P is out of supported range, the maximum value or
 119          * minimum value will applied automatically. So no need to check that.
 120          */
 121         freq = dev_priv->vbt.backlight.pwm_freq_hz;
 122         DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
 123         if (!freq) {
 124                 DRM_DEBUG_KMS("Use panel default backlight frequency\n");
 125                 return false;
 126         }
 127 
 128         fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
 129 
 130         /* Use highest possible value of Pn for more granularity of brightness
 131          * adjustment while satifying the conditions below.
 132          * - Pn is in the range of Pn_min and Pn_max
 133          * - F is in the range of 1 and 255
 134          * - FxP is within 25% of desired value.
 135          *   Note: 25% is arbitrary value and may need some tweak.
 136          */
 137         if (drm_dp_dpcd_readb(&intel_dp->aux,
 138                                DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
 139                 DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
 140                 return false;
 141         }
 142         if (drm_dp_dpcd_readb(&intel_dp->aux,
 143                                DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
 144                 DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
 145                 return false;
 146         }
 147         pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 148         pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 149 
 150         fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
 151         fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
 152         if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
 153                 DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
 154                 return false;
 155         }
 156 
 157         for (pn = pn_max; pn >= pn_min; pn--) {
 158                 f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
 159                 fxp_actual = f << pn;
 160                 if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
 161                         break;
 162         }
 163 
 164         if (drm_dp_dpcd_writeb(&intel_dp->aux,
 165                                DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
 166                 DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
 167                 return false;
 168         }
 169         if (drm_dp_dpcd_writeb(&intel_dp->aux,
 170                                DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
 171                 DRM_DEBUG_KMS("Failed to write aux backlight freq\n");
 172                 return false;
 173         }
 174         return true;
 175 }
 176 
 177 static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_state,
 178                                           const struct drm_connector_state *conn_state)
 179 {
 180         struct intel_connector *connector = to_intel_connector(conn_state->connector);
 181         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
 182         u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
 183 
 184         if (drm_dp_dpcd_readb(&intel_dp->aux,
 185                         DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
 186                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
 187                               DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
 188                 return;
 189         }
 190 
 191         new_dpcd_buf = dpcd_buf;
 192         edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 193 
 194         switch (edp_backlight_mode) {
 195         case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
 196         case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
 197         case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
 198                 new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 199                 new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
 200                 break;
 201 
 202         /* Do nothing when it is already DPCD mode */
 203         case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
 204         default:
 205                 break;
 206         }
 207 
 208         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP)
 209                 if (intel_dp_aux_set_pwm_freq(connector))
 210                         new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
 211 
 212         if (new_dpcd_buf != dpcd_buf) {
 213                 if (drm_dp_dpcd_writeb(&intel_dp->aux,
 214                         DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) {
 215                         DRM_DEBUG_KMS("Failed to write aux backlight mode\n");
 216                 }
 217         }
 218 
 219         set_aux_backlight_enable(intel_dp, true);
 220         intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
 221 }
 222 
 223 static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
 224 {
 225         set_aux_backlight_enable(enc_to_intel_dp(old_conn_state->best_encoder), false);
 226 }
 227 
 228 static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
 229                                         enum pipe pipe)
 230 {
 231         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
 232         struct intel_panel *panel = &connector->panel;
 233 
 234         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
 235                 panel->backlight.max = 0xFFFF;
 236         else
 237                 panel->backlight.max = 0xFF;
 238 
 239         panel->backlight.min = 0;
 240         panel->backlight.level = intel_dp_aux_get_backlight(connector);
 241 
 242         panel->backlight.enabled = panel->backlight.level != 0;
 243 
 244         return 0;
 245 }
 246 
 247 static bool
 248 intel_dp_aux_display_control_capable(struct intel_connector *connector)
 249 {
 250         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
 251 
 252         /* Check the eDP Display control capabilities registers to determine if
 253          * the panel can support backlight control over the aux channel
 254          */
 255         if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
 256             (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) &&
 257             !(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
 258                 DRM_DEBUG_KMS("AUX Backlight Control Supported!\n");
 259                 return true;
 260         }
 261         return false;
 262 }
 263 
 264 int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
 265 {
 266         struct intel_panel *panel = &intel_connector->panel;
 267         struct drm_i915_private *dev_priv = to_i915(intel_connector->base.dev);
 268 
 269         if (i915_modparams.enable_dpcd_backlight == 0 ||
 270             (i915_modparams.enable_dpcd_backlight == -1 &&
 271             dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE))
 272                 return -ENODEV;
 273 
 274         if (!intel_dp_aux_display_control_capable(intel_connector))
 275                 return -ENODEV;
 276 
 277         panel->backlight.setup = intel_dp_aux_setup_backlight;
 278         panel->backlight.enable = intel_dp_aux_enable_backlight;
 279         panel->backlight.disable = intel_dp_aux_disable_backlight;
 280         panel->backlight.set = intel_dp_aux_set_backlight;
 281         panel->backlight.get = intel_dp_aux_get_backlight;
 282 
 283         return 0;
 284 }

/* [<][>][^][v][top][bottom][index][help] */