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/*
  26 * Laptops with Intel GPUs which have panels that support controlling the
  27 * backlight through DP AUX can actually use two different interfaces: Intel's
  28 * proprietary DP AUX backlight interface, and the standard VESA backlight
  29 * interface. Unfortunately, at the time of writing this a lot of laptops will
  30 * advertise support for the standard VESA backlight interface when they
  31 * don't properly support it. However, on these systems the Intel backlight
  32 * interface generally does work properly. Additionally, these systems will
  33 * usually just indicate that they use PWM backlight controls in their VBIOS
  34 * for some reason.
  35 */
  36
  37#include "intel_display_types.h"
  38#include "intel_dp_aux_backlight.h"
  39#include "intel_panel.h"
  40
  41/* TODO:
  42 * Implement HDR, right now we just implement the bare minimum to bring us back into SDR mode so we
  43 * can make people's backlights work in the mean time
  44 */
  45
  46/*
  47 * DP AUX registers for Intel's proprietary HDR backlight interface. We define
  48 * them here since we'll likely be the only driver to ever use these.
  49 */
  50#define INTEL_EDP_HDR_TCON_CAP0                                        0x340
  51
  52#define INTEL_EDP_HDR_TCON_CAP1                                        0x341
  53# define INTEL_EDP_HDR_TCON_2084_DECODE_CAP                           BIT(0)
  54# define INTEL_EDP_HDR_TCON_2020_GAMUT_CAP                            BIT(1)
  55# define INTEL_EDP_HDR_TCON_TONE_MAPPING_CAP                          BIT(2)
  56# define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_CAP                   BIT(3)
  57# define INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP                       BIT(4)
  58# define INTEL_EDP_HDR_TCON_OPTIMIZATION_CAP                          BIT(5)
  59# define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_CAP                       BIT(6)
  60# define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_CONVERSION_CAP        BIT(7)
  61
  62#define INTEL_EDP_HDR_TCON_CAP2                                        0x342
  63# define INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP                        BIT(0)
  64
  65#define INTEL_EDP_HDR_TCON_CAP3                                        0x343
  66
  67#define INTEL_EDP_HDR_GETSET_CTRL_PARAMS                               0x344
  68# define INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE                        BIT(0)
  69# define INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE                         BIT(1)
  70# define INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE                       BIT(2) /* Pre-TGL+ */
  71# define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE                BIT(3)
  72# define INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE                     BIT(4)
  73# define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_ENABLE                BIT(5)
  74/* Bit 6 is reserved */
  75# define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_ENABLE                    BIT(7)
  76
  77#define INTEL_EDP_HDR_CONTENT_LUMINANCE                                0x346 /* Pre-TGL+ */
  78#define INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE                         0x34A
  79#define INTEL_EDP_SDR_LUMINANCE_LEVEL                                  0x352
  80#define INTEL_EDP_BRIGHTNESS_NITS_LSB                                  0x354
  81#define INTEL_EDP_BRIGHTNESS_NITS_MSB                                  0x355
  82#define INTEL_EDP_BRIGHTNESS_DELAY_FRAMES                              0x356
  83#define INTEL_EDP_BRIGHTNESS_PER_FRAME_STEPS                           0x357
  84
  85#define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_0                            0x358
  86# define INTEL_EDP_TCON_USAGE_MASK                             GENMASK(0, 3)
  87# define INTEL_EDP_TCON_USAGE_UNKNOWN                                    0x0
  88# define INTEL_EDP_TCON_USAGE_DESKTOP                                    0x1
  89# define INTEL_EDP_TCON_USAGE_FULL_SCREEN_MEDIA                          0x2
  90# define INTEL_EDP_TCON_USAGE_FULL_SCREEN_GAMING                         0x3
  91# define INTEL_EDP_TCON_POWER_MASK                                    BIT(4)
  92# define INTEL_EDP_TCON_POWER_DC                                    (0 << 4)
  93# define INTEL_EDP_TCON_POWER_AC                                    (1 << 4)
  94# define INTEL_EDP_TCON_OPTIMIZATION_STRENGTH_MASK             GENMASK(5, 7)
  95
  96#define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_1                            0x359
  97
  98/* Intel EDP backlight callbacks */
  99static bool
 100intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector)
 101{
 102        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 103        struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
 104        struct drm_dp_aux *aux = &intel_dp->aux;
 105        struct intel_panel *panel = &connector->panel;
 106        int ret;
 107        u8 tcon_cap[4];
 108
 109        ret = drm_dp_dpcd_read(aux, INTEL_EDP_HDR_TCON_CAP0, tcon_cap, sizeof(tcon_cap));
 110        if (ret < 0)
 111                return false;
 112
 113        if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP))
 114                return false;
 115
 116        if (tcon_cap[0] >= 1) {
 117                drm_dbg_kms(&i915->drm, "Detected Intel HDR backlight interface version %d\n",
 118                            tcon_cap[0]);
 119        } else {
 120                drm_dbg_kms(&i915->drm, "Detected unsupported HDR backlight interface version %d\n",
 121                            tcon_cap[0]);
 122                return false;
 123        }
 124
 125        panel->backlight.edp.intel.sdr_uses_aux =
 126                tcon_cap[2] & INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP;
 127
 128        return true;
 129}
 130
 131static u32
 132intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe)
 133{
 134        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 135        struct intel_panel *panel = &connector->panel;
 136        struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
 137        u8 tmp;
 138        u8 buf[2] = { 0 };
 139
 140        if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) < 0) {
 141                drm_err(&i915->drm, "Failed to read current backlight mode from DPCD\n");
 142                return 0;
 143        }
 144
 145        if (!(tmp & INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE)) {
 146                if (!panel->backlight.edp.intel.sdr_uses_aux) {
 147                        u32 pwm_level = panel->backlight.pwm_funcs->get(connector, pipe);
 148
 149                        return intel_panel_backlight_level_from_pwm(connector, pwm_level);
 150                }
 151
 152                /* Assume 100% brightness if backlight controls aren't enabled yet */
 153                return panel->backlight.max;
 154        }
 155
 156        if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, sizeof(buf)) < 0) {
 157                drm_err(&i915->drm, "Failed to read brightness from DPCD\n");
 158                return 0;
 159        }
 160
 161        return (buf[1] << 8 | buf[0]);
 162}
 163
 164static void
 165intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, u32 level)
 166{
 167        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 168        struct drm_device *dev = connector->base.dev;
 169        struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
 170        u8 buf[4] = { 0 };
 171
 172        buf[0] = level & 0xFF;
 173        buf[1] = (level & 0xFF00) >> 8;
 174
 175        if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, 4) < 0)
 176                drm_err(dev, "Failed to write brightness level to DPCD\n");
 177}
 178
 179static void
 180intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level)
 181{
 182        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 183        struct intel_panel *panel = &connector->panel;
 184
 185        if (panel->backlight.edp.intel.sdr_uses_aux) {
 186                intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
 187        } else {
 188                const u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level);
 189
 190                intel_panel_set_pwm_level(conn_state, pwm_level);
 191        }
 192}
 193
 194static void
 195intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state,
 196                                  const struct drm_connector_state *conn_state, u32 level)
 197{
 198        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 199        struct intel_panel *panel = &connector->panel;
 200        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 201        struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
 202        int ret;
 203        u8 old_ctrl, ctrl;
 204
 205        ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl);
 206        if (ret < 0) {
 207                drm_err(&i915->drm, "Failed to read current backlight control mode: %d\n", ret);
 208                return;
 209        }
 210
 211        ctrl = old_ctrl;
 212        if (panel->backlight.edp.intel.sdr_uses_aux) {
 213                ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
 214                intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
 215        } else {
 216                u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level);
 217
 218                panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
 219
 220                ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
 221        }
 222
 223        if (ctrl != old_ctrl)
 224                if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) < 0)
 225                        drm_err(&i915->drm, "Failed to configure DPCD brightness controls\n");
 226}
 227
 228static void
 229intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
 230{
 231        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 232        struct intel_panel *panel = &connector->panel;
 233
 234        /* Nothing to do for AUX based backlight controls */
 235        if (panel->backlight.edp.intel.sdr_uses_aux)
 236                return;
 237
 238        /* Note we want the actual pwm_level to be 0, regardless of pwm_min */
 239        panel->backlight.pwm_funcs->disable(conn_state, intel_panel_invert_pwm_level(connector, 0));
 240}
 241
 242static int
 243intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe)
 244{
 245        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 246        struct intel_panel *panel = &connector->panel;
 247        int ret;
 248
 249        if (panel->backlight.edp.intel.sdr_uses_aux) {
 250                drm_dbg_kms(&i915->drm, "SDR backlight is controlled through DPCD\n");
 251        } else {
 252                drm_dbg_kms(&i915->drm, "SDR backlight is controlled through PWM\n");
 253
 254                ret = panel->backlight.pwm_funcs->setup(connector, pipe);
 255                if (ret < 0) {
 256                        drm_err(&i915->drm,
 257                                "Failed to setup SDR backlight controls through PWM: %d\n", ret);
 258                        return ret;
 259                }
 260        }
 261
 262        panel->backlight.max = 512;
 263        panel->backlight.min = 0;
 264        panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe);
 265        panel->backlight.enabled = panel->backlight.level != 0;
 266
 267        return 0;
 268}
 269
 270/* VESA backlight callbacks */
 271static void set_vesa_backlight_enable(struct intel_dp *intel_dp, bool enable)
 272{
 273        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 274        u8 reg_val = 0;
 275
 276        /* Early return when display use other mechanism to enable backlight. */
 277        if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
 278                return;
 279
 280        if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
 281                              &reg_val) < 0) {
 282                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
 283                            DP_EDP_DISPLAY_CONTROL_REGISTER);
 284                return;
 285        }
 286        if (enable)
 287                reg_val |= DP_EDP_BACKLIGHT_ENABLE;
 288        else
 289                reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
 290
 291        if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
 292                               reg_val) != 1) {
 293                drm_dbg_kms(&i915->drm, "Failed to %s aux backlight\n",
 294                            enabledisable(enable));
 295        }
 296}
 297
 298static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connector)
 299{
 300        struct intel_dp *intel_dp = intel_attached_dp(connector);
 301        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 302        u8 mode_reg;
 303
 304        if (drm_dp_dpcd_readb(&intel_dp->aux,
 305                              DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
 306                              &mode_reg) != 1) {
 307                drm_dbg_kms(&i915->drm,
 308                            "Failed to read the DPCD register 0x%x\n",
 309                            DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
 310                return false;
 311        }
 312
 313        return (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) ==
 314               DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
 315}
 316
 317/*
 318 * Read the current backlight value from DPCD register(s) based
 319 * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
 320 */
 321static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused)
 322{
 323        struct intel_dp *intel_dp = intel_attached_dp(connector);
 324        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 325        u8 read_val[2] = { 0x0 };
 326        u16 level = 0;
 327
 328        /*
 329         * If we're not in DPCD control mode yet, the programmed brightness
 330         * value is meaningless and we should assume max brightness
 331         */
 332        if (!intel_dp_aux_vesa_backlight_dpcd_mode(connector))
 333                return connector->panel.backlight.max;
 334
 335        if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
 336                             &read_val, sizeof(read_val)) < 0) {
 337                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
 338                            DP_EDP_BACKLIGHT_BRIGHTNESS_MSB);
 339                return 0;
 340        }
 341        level = read_val[0];
 342        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
 343                level = (read_val[0] << 8 | read_val[1]);
 344
 345        return level;
 346}
 347
 348/*
 349 * Sends the current backlight level over the aux channel, checking if its using
 350 * 8-bit or 16 bit value (MSB and LSB)
 351 */
 352static void
 353intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state,
 354                                u32 level)
 355{
 356        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 357        struct intel_dp *intel_dp = intel_attached_dp(connector);
 358        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 359        u8 vals[2] = { 0x0 };
 360
 361        vals[0] = level;
 362
 363        /* Write the MSB and/or LSB */
 364        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) {
 365                vals[0] = (level & 0xFF00) >> 8;
 366                vals[1] = (level & 0xFF);
 367        }
 368        if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
 369                              vals, sizeof(vals)) < 0) {
 370                drm_dbg_kms(&i915->drm,
 371                            "Failed to write aux backlight level\n");
 372                return;
 373        }
 374}
 375
 376/*
 377 * Set PWM Frequency divider to match desired frequency in vbt.
 378 * The PWM Frequency is calculated as 27Mhz / (F x P).
 379 * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
 380 *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
 381 * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
 382 *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
 383 */
 384static bool intel_dp_aux_vesa_set_pwm_freq(struct intel_connector *connector)
 385{
 386        struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 387        struct intel_dp *intel_dp = intel_attached_dp(connector);
 388        const u8 pn = connector->panel.backlight.edp.vesa.pwmgen_bit_count;
 389        int freq, fxp, f, fxp_actual, fxp_min, fxp_max;
 390
 391        freq = dev_priv->vbt.backlight.pwm_freq_hz;
 392        if (!freq) {
 393                drm_dbg_kms(&dev_priv->drm,
 394                            "Use panel default backlight frequency\n");
 395                return false;
 396        }
 397
 398        fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
 399        f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
 400        fxp_actual = f << pn;
 401
 402        /* Ensure frequency is within 25% of desired value */
 403        fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
 404        fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
 405
 406        if (fxp_min > fxp_actual || fxp_actual > fxp_max) {
 407                drm_dbg_kms(&dev_priv->drm, "Actual frequency out of range\n");
 408                return false;
 409        }
 410
 411        if (drm_dp_dpcd_writeb(&intel_dp->aux,
 412                               DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
 413                drm_dbg_kms(&dev_priv->drm,
 414                            "Failed to write aux backlight freq\n");
 415                return false;
 416        }
 417        return true;
 418}
 419
 420static void
 421intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
 422                                   const struct drm_connector_state *conn_state, u32 level)
 423{
 424        struct intel_connector *connector = to_intel_connector(conn_state->connector);
 425        struct intel_dp *intel_dp = intel_attached_dp(connector);
 426        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 427        struct intel_panel *panel = &connector->panel;
 428        u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
 429        u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count;
 430
 431        if (drm_dp_dpcd_readb(&intel_dp->aux,
 432                        DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
 433                drm_dbg_kms(&i915->drm, "Failed to read DPCD register 0x%x\n",
 434                            DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
 435                return;
 436        }
 437
 438        new_dpcd_buf = dpcd_buf;
 439        edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 440
 441        switch (edp_backlight_mode) {
 442        case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
 443        case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
 444        case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
 445                new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
 446                new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
 447
 448                if (drm_dp_dpcd_writeb(&intel_dp->aux,
 449                                       DP_EDP_PWMGEN_BIT_COUNT,
 450                                       pwmgen_bit_count) < 0)
 451                        drm_dbg_kms(&i915->drm,
 452                                    "Failed to write aux pwmgen bit count\n");
 453
 454                break;
 455
 456        /* Do nothing when it is already DPCD mode */
 457        case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
 458        default:
 459                break;
 460        }
 461
 462        if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP)
 463                if (intel_dp_aux_vesa_set_pwm_freq(connector))
 464                        new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
 465
 466        if (new_dpcd_buf != dpcd_buf) {
 467                if (drm_dp_dpcd_writeb(&intel_dp->aux,
 468                        DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) {
 469                        drm_dbg_kms(&i915->drm,
 470                                    "Failed to write aux backlight mode\n");
 471                }
 472        }
 473
 474        intel_dp_aux_vesa_set_backlight(conn_state, level);
 475        set_vesa_backlight_enable(intel_dp, true);
 476}
 477
 478static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state,
 479                                                u32 level)
 480{
 481        set_vesa_backlight_enable(enc_to_intel_dp(to_intel_encoder(old_conn_state->best_encoder)),
 482                                  false);
 483}
 484
 485static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connector)
 486{
 487        struct drm_i915_private *i915 = to_i915(connector->base.dev);
 488        struct intel_dp *intel_dp = intel_attached_dp(connector);
 489        struct intel_panel *panel = &connector->panel;
 490        u32 max_backlight = 0;
 491        int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
 492        u8 pn, pn_min, pn_max;
 493
 494        if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT, &pn) == 1) {
 495                pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 496                max_backlight = (1 << pn) - 1;
 497        }
 498
 499        /* Find desired value of (F x P)
 500         * Note that, if F x P is out of supported range, the maximum value or
 501         * minimum value will applied automatically. So no need to check that.
 502         */
 503        freq = i915->vbt.backlight.pwm_freq_hz;
 504        drm_dbg_kms(&i915->drm, "VBT defined backlight frequency %u Hz\n",
 505                    freq);
 506        if (!freq) {
 507                drm_dbg_kms(&i915->drm,
 508                            "Use panel default backlight frequency\n");
 509                return max_backlight;
 510        }
 511
 512        fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
 513
 514        /* Use highest possible value of Pn for more granularity of brightness
 515         * adjustment while satifying the conditions below.
 516         * - Pn is in the range of Pn_min and Pn_max
 517         * - F is in the range of 1 and 255
 518         * - FxP is within 25% of desired value.
 519         *   Note: 25% is arbitrary value and may need some tweak.
 520         */
 521        if (drm_dp_dpcd_readb(&intel_dp->aux,
 522                              DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
 523                drm_dbg_kms(&i915->drm,
 524                            "Failed to read pwmgen bit count cap min\n");
 525                return max_backlight;
 526        }
 527        if (drm_dp_dpcd_readb(&intel_dp->aux,
 528                              DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
 529                drm_dbg_kms(&i915->drm,
 530                            "Failed to read pwmgen bit count cap max\n");
 531                return max_backlight;
 532        }
 533        pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 534        pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
 535
 536        fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
 537        fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
 538        if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
 539                drm_dbg_kms(&i915->drm,
 540                            "VBT defined backlight frequency out of range\n");
 541                return max_backlight;
 542        }
 543
 544        for (pn = pn_max; pn >= pn_min; pn--) {
 545                f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
 546                fxp_actual = f << pn;
 547                if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
 548                        break;
 549        }
 550
 551        drm_dbg_kms(&i915->drm, "Using eDP pwmgen bit count of %d\n", pn);
 552        if (drm_dp_dpcd_writeb(&intel_dp->aux,
 553                               DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
 554                drm_dbg_kms(&i915->drm,
 555                            "Failed to write aux pwmgen bit count\n");
 556                return max_backlight;
 557        }
 558        panel->backlight.edp.vesa.pwmgen_bit_count = pn;
 559
 560        max_backlight = (1 << pn) - 1;
 561
 562        return max_backlight;
 563}
 564
 565static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
 566                                             enum pipe pipe)
 567{
 568        struct intel_panel *panel = &connector->panel;
 569
 570        panel->backlight.max = intel_dp_aux_vesa_calc_max_backlight(connector);
 571        if (!panel->backlight.max)
 572                return -ENODEV;
 573
 574        panel->backlight.min = 0;
 575        panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, pipe);
 576        panel->backlight.enabled = intel_dp_aux_vesa_backlight_dpcd_mode(connector) &&
 577                                   panel->backlight.level != 0;
 578
 579        return 0;
 580}
 581
 582static bool
 583intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector)
 584{
 585        struct intel_dp *intel_dp = intel_attached_dp(connector);
 586        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 587
 588        /* Check the eDP Display control capabilities registers to determine if
 589         * the panel can support backlight control over the aux channel.
 590         *
 591         * TODO: We currently only support AUX only backlight configurations, not backlights which
 592         * require a mix of PWM and AUX controls to work. In the mean time, these machines typically
 593         * work just fine using normal PWM controls anyway.
 594         */
 595        if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
 596            (intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
 597            (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {
 598                drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n");
 599                return true;
 600        }
 601        return false;
 602}
 603
 604static const struct intel_panel_bl_funcs intel_dp_hdr_bl_funcs = {
 605        .setup = intel_dp_aux_hdr_setup_backlight,
 606        .enable = intel_dp_aux_hdr_enable_backlight,
 607        .disable = intel_dp_aux_hdr_disable_backlight,
 608        .set = intel_dp_aux_hdr_set_backlight,
 609        .get = intel_dp_aux_hdr_get_backlight,
 610};
 611
 612static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = {
 613        .setup = intel_dp_aux_vesa_setup_backlight,
 614        .enable = intel_dp_aux_vesa_enable_backlight,
 615        .disable = intel_dp_aux_vesa_disable_backlight,
 616        .set = intel_dp_aux_vesa_set_backlight,
 617        .get = intel_dp_aux_vesa_get_backlight,
 618};
 619
 620enum intel_dp_aux_backlight_modparam {
 621        INTEL_DP_AUX_BACKLIGHT_AUTO = -1,
 622        INTEL_DP_AUX_BACKLIGHT_OFF = 0,
 623        INTEL_DP_AUX_BACKLIGHT_ON = 1,
 624        INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2,
 625        INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3,
 626};
 627
 628int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
 629{
 630        struct drm_device *dev = connector->base.dev;
 631        struct intel_panel *panel = &connector->panel;
 632        struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
 633        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
 634        bool try_intel_interface = false, try_vesa_interface = false;
 635
 636        /* Check the VBT and user's module parameters to figure out which
 637         * interfaces to probe
 638         */
 639        switch (i915->params.enable_dpcd_backlight) {
 640        case INTEL_DP_AUX_BACKLIGHT_OFF:
 641                return -ENODEV;
 642        case INTEL_DP_AUX_BACKLIGHT_AUTO:
 643                switch (i915->vbt.backlight.type) {
 644                case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE:
 645                        try_vesa_interface = true;
 646                        break;
 647                case INTEL_BACKLIGHT_DISPLAY_DDI:
 648                        try_intel_interface = true;
 649                        break;
 650                default:
 651                        return -ENODEV;
 652                }
 653                break;
 654        case INTEL_DP_AUX_BACKLIGHT_ON:
 655                if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
 656                        try_intel_interface = true;
 657
 658                try_vesa_interface = true;
 659                break;
 660        case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA:
 661                try_vesa_interface = true;
 662                break;
 663        case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL:
 664                try_intel_interface = true;
 665                break;
 666        }
 667
 668        /*
 669         * A lot of eDP panels in the wild will report supporting both the
 670         * Intel proprietary backlight control interface, and the VESA
 671         * backlight control interface. Many of these panels are liars though,
 672         * and will only work with the Intel interface. So, always probe for
 673         * that first.
 674         */
 675        if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) {
 676                drm_dbg_kms(dev, "Using Intel proprietary eDP backlight controls\n");
 677                panel->backlight.funcs = &intel_dp_hdr_bl_funcs;
 678                return 0;
 679        }
 680
 681        if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) {
 682                drm_dbg_kms(dev, "Using VESA eDP backlight controls\n");
 683                panel->backlight.funcs = &intel_dp_vesa_bl_funcs;
 684                return 0;
 685        }
 686
 687        return -ENODEV;
 688}
 689