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 */ 99 static bool 100 intel_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 != sizeof(tcon_cap)) 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 131 static u32 132 intel_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) != 1) { 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, 157 sizeof(buf)) != sizeof(buf)) { 158 drm_err(&i915->drm, "Failed to read brightness from DPCD\n"); 159 return 0; 160 } 161 162 return (buf[1] << 8 | buf[0]); 163 } 164 165 static void 166 intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, u32 level) 167 { 168 struct intel_connector *connector = to_intel_connector(conn_state->connector); 169 struct drm_device *dev = connector->base.dev; 170 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 171 u8 buf[4] = { 0 }; 172 173 buf[0] = level & 0xFF; 174 buf[1] = (level & 0xFF00) >> 8; 175 176 if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, 177 sizeof(buf)) != sizeof(buf)) 178 drm_err(dev, "Failed to write brightness level to DPCD\n"); 179 } 180 181 static void 182 intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level) 183 { 184 struct intel_connector *connector = to_intel_connector(conn_state->connector); 185 struct intel_panel *panel = &connector->panel; 186 187 if (panel->backlight.edp.intel.sdr_uses_aux) { 188 intel_dp_aux_hdr_set_aux_backlight(conn_state, level); 189 } else { 190 const u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level); 191 192 intel_panel_set_pwm_level(conn_state, pwm_level); 193 } 194 } 195 196 static void 197 intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state, 198 const struct drm_connector_state *conn_state, u32 level) 199 { 200 struct intel_connector *connector = to_intel_connector(conn_state->connector); 201 struct intel_panel *panel = &connector->panel; 202 struct drm_i915_private *i915 = to_i915(connector->base.dev); 203 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 204 int ret; 205 u8 old_ctrl, ctrl; 206 207 ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl); 208 if (ret != 1) { 209 drm_err(&i915->drm, "Failed to read current backlight control mode: %d\n", ret); 210 return; 211 } 212 213 ctrl = old_ctrl; 214 if (panel->backlight.edp.intel.sdr_uses_aux) { 215 ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE; 216 intel_dp_aux_hdr_set_aux_backlight(conn_state, level); 217 } else { 218 u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level); 219 220 panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level); 221 222 ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE; 223 } 224 225 if (ctrl != old_ctrl) 226 if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) != 1) 227 drm_err(&i915->drm, "Failed to configure DPCD brightness controls\n"); 228 } 229 230 static void 231 intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, u32 level) 232 { 233 struct intel_connector *connector = to_intel_connector(conn_state->connector); 234 struct intel_panel *panel = &connector->panel; 235 236 /* Nothing to do for AUX based backlight controls */ 237 if (panel->backlight.edp.intel.sdr_uses_aux) 238 return; 239 240 /* Note we want the actual pwm_level to be 0, regardless of pwm_min */ 241 panel->backlight.pwm_funcs->disable(conn_state, intel_panel_invert_pwm_level(connector, 0)); 242 } 243 244 static int 245 intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe) 246 { 247 struct drm_i915_private *i915 = to_i915(connector->base.dev); 248 struct intel_panel *panel = &connector->panel; 249 int ret; 250 251 if (panel->backlight.edp.intel.sdr_uses_aux) { 252 drm_dbg_kms(&i915->drm, "SDR backlight is controlled through DPCD\n"); 253 } else { 254 drm_dbg_kms(&i915->drm, "SDR backlight is controlled through PWM\n"); 255 256 ret = panel->backlight.pwm_funcs->setup(connector, pipe); 257 if (ret < 0) { 258 drm_err(&i915->drm, 259 "Failed to setup SDR backlight controls through PWM: %d\n", ret); 260 return ret; 261 } 262 } 263 264 panel->backlight.max = 512; 265 panel->backlight.min = 0; 266 panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe); 267 panel->backlight.enabled = panel->backlight.level != 0; 268 269 return 0; 270 } 271 272 /* VESA backlight callbacks */ 273 static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused) 274 { 275 return connector->panel.backlight.level; 276 } 277 278 static void 279 intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level) 280 { 281 struct intel_connector *connector = to_intel_connector(conn_state->connector); 282 struct intel_panel *panel = &connector->panel; 283 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 284 285 drm_edp_backlight_set_level(&intel_dp->aux, &panel->backlight.edp.vesa.info, level); 286 } 287 288 static void 289 intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state, 290 const struct drm_connector_state *conn_state, u32 level) 291 { 292 struct intel_connector *connector = to_intel_connector(conn_state->connector); 293 struct intel_panel *panel = &connector->panel; 294 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 295 296 drm_edp_backlight_enable(&intel_dp->aux, &panel->backlight.edp.vesa.info, level); 297 } 298 299 static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state, 300 u32 level) 301 { 302 struct intel_connector *connector = to_intel_connector(old_conn_state->connector); 303 struct intel_panel *panel = &connector->panel; 304 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 305 306 drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info); 307 } 308 309 static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe) 310 { 311 struct intel_dp *intel_dp = intel_attached_dp(connector); 312 struct intel_panel *panel = &connector->panel; 313 struct drm_i915_private *i915 = dp_to_i915(intel_dp); 314 u16 current_level; 315 u8 current_mode; 316 int ret; 317 318 ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info, 319 i915->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd, 320 ¤t_level, ¤t_mode); 321 if (ret < 0) 322 return ret; 323 324 panel->backlight.max = panel->backlight.edp.vesa.info.max; 325 panel->backlight.min = 0; 326 if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { 327 panel->backlight.level = current_level; 328 panel->backlight.enabled = panel->backlight.level != 0; 329 } else { 330 panel->backlight.level = panel->backlight.max; 331 panel->backlight.enabled = false; 332 } 333 334 return 0; 335 } 336 337 static bool 338 intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector) 339 { 340 struct intel_dp *intel_dp = intel_attached_dp(connector); 341 struct drm_i915_private *i915 = dp_to_i915(intel_dp); 342 343 /* TODO: We currently only support AUX only backlight configurations, not backlights which 344 * require a mix of PWM and AUX controls to work. In the mean time, these machines typically 345 * work just fine using normal PWM controls anyway. 346 */ 347 if ((intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) && 348 drm_edp_backlight_supported(intel_dp->edp_dpcd)) { 349 drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n"); 350 return true; 351 } 352 return false; 353 } 354 355 static const struct intel_panel_bl_funcs intel_dp_hdr_bl_funcs = { 356 .setup = intel_dp_aux_hdr_setup_backlight, 357 .enable = intel_dp_aux_hdr_enable_backlight, 358 .disable = intel_dp_aux_hdr_disable_backlight, 359 .set = intel_dp_aux_hdr_set_backlight, 360 .get = intel_dp_aux_hdr_get_backlight, 361 }; 362 363 static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = { 364 .setup = intel_dp_aux_vesa_setup_backlight, 365 .enable = intel_dp_aux_vesa_enable_backlight, 366 .disable = intel_dp_aux_vesa_disable_backlight, 367 .set = intel_dp_aux_vesa_set_backlight, 368 .get = intel_dp_aux_vesa_get_backlight, 369 }; 370 371 enum intel_dp_aux_backlight_modparam { 372 INTEL_DP_AUX_BACKLIGHT_AUTO = -1, 373 INTEL_DP_AUX_BACKLIGHT_OFF = 0, 374 INTEL_DP_AUX_BACKLIGHT_ON = 1, 375 INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2, 376 INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3, 377 }; 378 379 int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) 380 { 381 struct drm_device *dev = connector->base.dev; 382 struct intel_panel *panel = &connector->panel; 383 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); 384 struct drm_i915_private *i915 = dp_to_i915(intel_dp); 385 bool try_intel_interface = false, try_vesa_interface = false; 386 387 /* Check the VBT and user's module parameters to figure out which 388 * interfaces to probe 389 */ 390 switch (i915->params.enable_dpcd_backlight) { 391 case INTEL_DP_AUX_BACKLIGHT_OFF: 392 return -ENODEV; 393 case INTEL_DP_AUX_BACKLIGHT_AUTO: 394 switch (i915->vbt.backlight.type) { 395 case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE: 396 try_vesa_interface = true; 397 break; 398 case INTEL_BACKLIGHT_DISPLAY_DDI: 399 try_intel_interface = true; 400 break; 401 default: 402 return -ENODEV; 403 } 404 break; 405 case INTEL_DP_AUX_BACKLIGHT_ON: 406 if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE) 407 try_intel_interface = true; 408 409 try_vesa_interface = true; 410 break; 411 case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA: 412 try_vesa_interface = true; 413 break; 414 case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: 415 try_intel_interface = true; 416 break; 417 } 418 419 /* 420 * A lot of eDP panels in the wild will report supporting both the 421 * Intel proprietary backlight control interface, and the VESA 422 * backlight control interface. Many of these panels are liars though, 423 * and will only work with the Intel interface. So, always probe for 424 * that first. 425 */ 426 if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) { 427 drm_dbg_kms(dev, "Using Intel proprietary eDP backlight controls\n"); 428 panel->backlight.funcs = &intel_dp_hdr_bl_funcs; 429 return 0; 430 } 431 432 if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) { 433 drm_dbg_kms(dev, "Using VESA eDP backlight controls\n"); 434 panel->backlight.funcs = &intel_dp_vesa_bl_funcs; 435 return 0; 436 } 437 438 return -ENODEV; 439 } 440