linux/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
<<
>>
Prefs
   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
  28static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
  29{
  30        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
  31        u8 reg_val = 0;
  32
  33        /* Early return when display use other mechanism to enable backlight. */
  34        if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
  35                return;
  36
  37        if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
  38                              &reg_val) < 0) {
  39                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
  40                            DP_EDP_DISPLAY_CONTROL_REGISTER);
  41                return;
  42        }
  43        if (enable)
  44                reg_val |= DP_EDP_BACKLIGHT_ENABLE;
  45        else
  46                reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
  47
  48        if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
  49                               reg_val) != 1) {
  50                drm_dbg_kms(&i915->drm, "Failed to %s aux backlight\n",
  51                            enable ? "enable" : "disable");
  52        }
  53}
  54
  55static bool intel_dp_aux_backlight_dpcd_mode(struct intel_connector *connector)
  56{
  57        struct intel_dp *intel_dp = intel_attached_dp(connector);
  58        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
  59        u8 mode_reg;
  60
  61        if (drm_dp_dpcd_readb(&intel_dp->aux,
  62                              DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
  63                              &mode_reg) != 1) {
  64                drm_dbg_kms(&i915->drm,
  65                            "Failed to read the DPCD register 0x%x\n",
  66                            DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
  67                return false;
  68        }
  69
  70        return (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) ==
  71               DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
  72}
  73
  74/*
  75 * Read the current backlight value from DPCD register(s) based
  76 * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
  77 */
  78static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
  79{
  80        struct intel_dp *intel_dp = intel_attached_dp(connector);
  81        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
  82        u8 read_val[2] = { 0x0 };
  83        u16 level = 0;
  84
  85        /*
  86         * If we're not in DPCD control mode yet, the programmed brightness
  87         * value is meaningless and we should assume max brightness
  88         */
  89        if (!intel_dp_aux_backlight_dpcd_mode(connector))
  90                return connector->panel.backlight.max;
  91
  92        if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
  93                             &read_val, sizeof(read_val)) < 0) {
  94                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
  95                            DP_EDP_BACKLIGHT_BRIGHTNESS_MSB);
  96                return 0;
  97        }
  98        level = read_val[0];
  99        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
 100                level = (read_val[0] << 8 | read_val[1]);
 101
 102        return level;
 103}
 104
 105/*
 106 * Sends the current backlight level over the aux channel, checking if its using
 107 * 8-bit or 16 bit value (MSB and LSB)
 108 */
 109static void
 110intel_dp_aux_set_backlight(const struct drm_connector_state *conn_state, u32 level)
 111{
 112        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 113        struct intel_dp *intel_dp = intel_attached_dp(connector);
 114        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 115        u8 vals[2] = { 0x0 };
 116
 117        vals[0] = level;
 118
 119        /* Write the MSB and/or LSB */
 120        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) {
 121                vals[0] = (level & 0xFF00) >> 8;
 122                vals[1] = (level & 0xFF);
 123        }
 124        if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
 125                              vals, sizeof(vals)) < 0) {
 126                drm_dbg_kms(&i915->drm,
 127                            "Failed to write aux backlight level\n");
 128                return;
 129        }
 130}
 131
 132/*
 133 * Set PWM Frequency divider to match desired frequency in vbt.
 134 * The PWM Frequency is calculated as 27Mhz / (F x P).
 135 * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
 136 *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
 137 * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
 138 *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
 139 */
 140static bool intel_dp_aux_set_pwm_freq(struct intel_connector *connector)
 141{
 142        struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 143        struct intel_dp *intel_dp = intel_attached_dp(connector);
 144        const u8 pn = connector->panel.backlight.pwmgen_bit_count;
 145        int freq, fxp, f, fxp_actual, fxp_min, fxp_max;
 146
 147        freq = dev_priv->vbt.backlight.pwm_freq_hz;
 148        if (!freq) {
 149                drm_dbg_kms(&dev_priv->drm,
 150                            "Use panel default backlight frequency\n");
 151                return false;
 152        }
 153
 154        fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
 155        f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
 156        fxp_actual = f << pn;
 157
 158        /* Ensure frequency is within 25% of desired value */
 159        fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
 160        fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
 161
 162        if (fxp_min > fxp_actual || fxp_actual > fxp_max) {
 163                drm_dbg_kms(&dev_priv->drm, "Actual frequency out of range\n");
 164                return false;
 165        }
 166
 167        if (drm_dp_dpcd_writeb(&intel_dp->aux,
 168                               DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
 169                drm_dbg_kms(&dev_priv->drm,
 170                            "Failed to write aux backlight freq\n");
 171                return false;
 172        }
 173        return true;
 174}
 175
 176static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_state,
 177                                          const struct drm_connector_state *conn_state)
 178{
 179        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 180        struct intel_dp *intel_dp = intel_attached_dp(connector);
 181        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 182        struct intel_panel *panel = &connector->panel;
 183        u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
 184
 185        if (drm_dp_dpcd_readb(&intel_dp->aux,
 186                        DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
 187                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
 188                            DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
 189                return;
 190        }
 191
 192        new_dpcd_buf = dpcd_buf;
 193        edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 194
 195        switch (edp_backlight_mode) {
 196        case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
 197        case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
 198        case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
 199                new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 200                new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
 201
 202                if (drm_dp_dpcd_writeb(&intel_dp->aux,
 203                                       DP_EDP_PWMGEN_BIT_COUNT,
 204                                       panel->backlight.pwmgen_bit_count) < 0)
 205                        drm_dbg_kms(&i915->drm,
 206                                    "Failed to write aux pwmgen bit count\n");
 207
 208                break;
 209
 210        /* Do nothing when it is already DPCD mode */
 211        case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
 212        default:
 213                break;
 214        }
 215
 216        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP)
 217                if (intel_dp_aux_set_pwm_freq(connector))
 218                        new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
 219
 220        if (new_dpcd_buf != dpcd_buf) {
 221                if (drm_dp_dpcd_writeb(&intel_dp->aux,
 222                        DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) {
 223                        drm_dbg_kms(&i915->drm,
 224                                    "Failed to write aux backlight mode\n");
 225                }
 226        }
 227
 228        intel_dp_aux_set_backlight(conn_state,
 229                                   connector->panel.backlight.level);
 230        set_aux_backlight_enable(intel_dp, true);
 231}
 232
 233static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
 234{
 235        set_aux_backlight_enable(enc_to_intel_dp(to_intel_encoder(old_conn_state->best_encoder)),
 236                                 false);
 237}
 238
 239static u32 intel_dp_aux_calc_max_backlight(struct intel_connector *connector)
 240{
 241        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 242        struct intel_dp *intel_dp = intel_attached_dp(connector);
 243        struct intel_panel *panel = &connector->panel;
 244        u32 max_backlight = 0;
 245        int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
 246        u8 pn, pn_min, pn_max;
 247
 248        if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn) == 1) {
 249                pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 250                max_backlight = (1 << pn) - 1;
 251        }
 252
 253        /* Find desired value of (F x P)
 254         * Note that, if F x P is out of supported range, the maximum value or
 255         * minimum value will applied automatically. So no need to check that.
 256         */
 257        freq = i915->vbt.backlight.pwm_freq_hz;
 258        drm_dbg_kms(&i915->drm, "VBT defined backlight frequency %u Hz\n",
 259                    freq);
 260        if (!freq) {
 261                drm_dbg_kms(&i915->drm,
 262                            "Use panel default backlight frequency\n");
 263                return max_backlight;
 264        }
 265
 266        fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
 267
 268        /* Use highest possible value of Pn for more granularity of brightness
 269         * adjustment while satifying the conditions below.
 270         * - Pn is in the range of Pn_min and Pn_max
 271         * - F is in the range of 1 and 255
 272         * - FxP is within 25% of desired value.
 273         *   Note: 25% is arbitrary value and may need some tweak.
 274         */
 275        if (drm_dp_dpcd_readb(&intel_dp->aux,
 276                              DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
 277                drm_dbg_kms(&i915->drm,
 278                            "Failed to read pwmgen bit count cap min\n");
 279                return max_backlight;
 280        }
 281        if (drm_dp_dpcd_readb(&intel_dp->aux,
 282                              DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
 283                drm_dbg_kms(&i915->drm,
 284                            "Failed to read pwmgen bit count cap max\n");
 285                return max_backlight;
 286        }
 287        pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 288        pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 289
 290        fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
 291        fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
 292        if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
 293                drm_dbg_kms(&i915->drm,
 294                            "VBT defined backlight frequency out of range\n");
 295                return max_backlight;
 296        }
 297
 298        for (pn = pn_max; pn >= pn_min; pn--) {
 299                f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
 300                fxp_actual = f << pn;
 301                if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
 302                        break;
 303        }
 304
 305        drm_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn);
 306        if (drm_dp_dpcd_writeb(&intel_dp->aux,
 307                               DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
 308                drm_dbg_kms(&i915->drm,
 309                            "Failed to write aux pwmgen bit count\n");
 310                return max_backlight;
 311        }
 312        panel->backlight.pwmgen_bit_count = pn;
 313
 314        max_backlight = (1 << pn) - 1;
 315
 316        return max_backlight;
 317}
 318
 319static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
 320                                        enum pipe pipe)
 321{
 322        struct intel_panel *panel = &connector->panel;
 323
 324        panel->backlight.max = intel_dp_aux_calc_max_backlight(connector);
 325        if (!panel->backlight.max)
 326                return -ENODEV;
 327
 328        panel->backlight.min = 0;
 329        panel->backlight.level = intel_dp_aux_get_backlight(connector);
 330        panel->backlight.enabled = intel_dp_aux_backlight_dpcd_mode(connector) &&
 331                                   panel->backlight.level != 0;
 332
 333        return 0;
 334}
 335
 336static bool
 337intel_dp_aux_display_control_capable(struct intel_connector *connector)
 338{
 339        struct intel_dp *intel_dp = intel_attached_dp(connector);
 340        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 341
 342        /* Check the eDP Display control capabilities registers to determine if
 343         * the panel can support backlight control over the aux channel
 344         */
 345        if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
 346            (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {
 347                drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n");
 348                return true;
 349        }
 350        return false;
 351}
 352
 353int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
 354{
 355        struct intel_panel *panel = &intel_connector->panel;
 356        struct intel_dp *intel_dp = enc_to_intel_dp(intel_connector->encoder);
 357        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 358
 359        if (i915->params.enable_dpcd_backlight == 0 ||
 360            !intel_dp_aux_display_control_capable(intel_connector))
 361                return -ENODEV;
 362
 363        /*
 364         * There are a lot of machines that don't advertise the backlight
 365         * control interface to use properly in their VBIOS, :\
 366         */
 367        if (i915->vbt.backlight.type !=
 368            INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
 369            i915->params.enable_dpcd_backlight != 1 &&
 370            !drm_dp_has_quirk(&intel_dp->desc, intel_dp->edid_quirks,
 371                              DP_QUIRK_FORCE_DPCD_BACKLIGHT)) {
 372                drm_info(&i915->drm,
 373                         "Panel advertises DPCD backlight support, but "
 374                         "VBT disagrees. If your backlight controls "
 375                         "don't work try booting with "
 376                         "i915.enable_dpcd_backlight=1. If your machine "
 377                         "needs this, please file a _new_ bug report on "
 378                         "drm/i915, see " FDO_BUG_URL " for details.\n");
 379                return -ENODEV;
 380        }
 381
 382        panel->backlight.setup = intel_dp_aux_setup_backlight;
 383        panel->backlight.enable = intel_dp_aux_enable_backlight;
 384        panel->backlight.disable = intel_dp_aux_disable_backlight;
 385        panel->backlight.set = intel_dp_aux_set_backlight;
 386        panel->backlight.get = intel_dp_aux_get_backlight;
 387
 388        return 0;
 389}
 390