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_backlight.h"
38 #include "intel_display_types.h"
39 #include "intel_dp.h"
40 #include "intel_dp_aux_backlight.h"
41
42 /* TODO:
43 * Implement HDR, right now we just implement the bare minimum to bring us back into SDR mode so we
44 * can make people's backlights work in the mean time
45 */
46
47 /*
48 * DP AUX registers for Intel's proprietary HDR backlight interface. We define
49 * them here since we'll likely be the only driver to ever use these.
50 */
51 #define INTEL_EDP_HDR_TCON_CAP0 0x340
52
53 #define INTEL_EDP_HDR_TCON_CAP1 0x341
54 # define INTEL_EDP_HDR_TCON_2084_DECODE_CAP BIT(0)
55 # define INTEL_EDP_HDR_TCON_2020_GAMUT_CAP BIT(1)
56 # define INTEL_EDP_HDR_TCON_TONE_MAPPING_CAP BIT(2)
57 # define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_CAP BIT(3)
58 # define INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP BIT(4)
59 # define INTEL_EDP_HDR_TCON_OPTIMIZATION_CAP BIT(5)
60 # define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_CAP BIT(6)
61 # define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_CONVERSION_CAP BIT(7)
62
63 #define INTEL_EDP_HDR_TCON_CAP2 0x342
64 # define INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP BIT(0)
65
66 #define INTEL_EDP_HDR_TCON_CAP3 0x343
67
68 #define INTEL_EDP_HDR_GETSET_CTRL_PARAMS 0x344
69 # define INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE BIT(0)
70 # define INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE BIT(1)
71 # define INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE BIT(2) /* Pre-TGL+ */
72 # define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE BIT(3)
73 # define INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE BIT(4)
74 # define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_ENABLE BIT(5)
75 /* Bit 6 is reserved */
76 # define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_ENABLE BIT(7)
77
78 #define INTEL_EDP_HDR_CONTENT_LUMINANCE 0x346 /* Pre-TGL+ */
79 #define INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE 0x34A
80 #define INTEL_EDP_SDR_LUMINANCE_LEVEL 0x352
81 #define INTEL_EDP_BRIGHTNESS_NITS_LSB 0x354
82 #define INTEL_EDP_BRIGHTNESS_NITS_MSB 0x355
83 #define INTEL_EDP_BRIGHTNESS_DELAY_FRAMES 0x356
84 #define INTEL_EDP_BRIGHTNESS_PER_FRAME_STEPS 0x357
85
86 #define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_0 0x358
87 # define INTEL_EDP_TCON_USAGE_MASK GENMASK(0, 3)
88 # define INTEL_EDP_TCON_USAGE_UNKNOWN 0x0
89 # define INTEL_EDP_TCON_USAGE_DESKTOP 0x1
90 # define INTEL_EDP_TCON_USAGE_FULL_SCREEN_MEDIA 0x2
91 # define INTEL_EDP_TCON_USAGE_FULL_SCREEN_GAMING 0x3
92 # define INTEL_EDP_TCON_POWER_MASK BIT(4)
93 # define INTEL_EDP_TCON_POWER_DC (0 << 4)
94 # define INTEL_EDP_TCON_POWER_AC (1 << 4)
95 # define INTEL_EDP_TCON_OPTIMIZATION_STRENGTH_MASK GENMASK(5, 7)
96
97 #define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_1 0x359
98
99 /* Intel EDP backlight callbacks */
100 static bool
intel_dp_aux_supports_hdr_backlight(struct intel_connector * connector)101 intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector)
102 {
103 struct drm_i915_private *i915 = to_i915(connector->base.dev);
104 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
105 struct drm_dp_aux *aux = &intel_dp->aux;
106 struct intel_panel *panel = &connector->panel;
107 int ret;
108 u8 tcon_cap[4];
109
110 intel_dp_wait_source_oui(intel_dp);
111
112 ret = drm_dp_dpcd_read(aux, INTEL_EDP_HDR_TCON_CAP0, tcon_cap, sizeof(tcon_cap));
113 if (ret != sizeof(tcon_cap))
114 return false;
115
116 if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP))
117 return false;
118
119 if (tcon_cap[0] >= 1) {
120 drm_dbg_kms(&i915->drm, "Detected Intel HDR backlight interface version %d\n",
121 tcon_cap[0]);
122 } else {
123 drm_dbg_kms(&i915->drm, "Detected unsupported HDR backlight interface version %d\n",
124 tcon_cap[0]);
125 return false;
126 }
127
128 panel->backlight.edp.intel.sdr_uses_aux =
129 tcon_cap[2] & INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP;
130
131 return true;
132 }
133
134 static u32
intel_dp_aux_hdr_get_backlight(struct intel_connector * connector,enum pipe pipe)135 intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe)
136 {
137 struct drm_i915_private *i915 = to_i915(connector->base.dev);
138 struct intel_panel *panel = &connector->panel;
139 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
140 u8 tmp;
141 u8 buf[2] = { 0 };
142
143 if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) != 1) {
144 drm_err(&i915->drm, "Failed to read current backlight mode from DPCD\n");
145 return 0;
146 }
147
148 if (!(tmp & INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE)) {
149 if (!panel->backlight.edp.intel.sdr_uses_aux) {
150 u32 pwm_level = panel->backlight.pwm_funcs->get(connector, pipe);
151
152 return intel_backlight_level_from_pwm(connector, pwm_level);
153 }
154
155 /* Assume 100% brightness if backlight controls aren't enabled yet */
156 return panel->backlight.max;
157 }
158
159 if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf,
160 sizeof(buf)) != sizeof(buf)) {
161 drm_err(&i915->drm, "Failed to read brightness from DPCD\n");
162 return 0;
163 }
164
165 return (buf[1] << 8 | buf[0]);
166 }
167
168 static void
intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state * conn_state,u32 level)169 intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, u32 level)
170 {
171 struct intel_connector *connector = to_intel_connector(conn_state->connector);
172 struct drm_device *dev = connector->base.dev;
173 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
174 u8 buf[4] = { 0 };
175
176 buf[0] = level & 0xFF;
177 buf[1] = (level & 0xFF00) >> 8;
178
179 if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf,
180 sizeof(buf)) != sizeof(buf))
181 drm_err(dev, "Failed to write brightness level to DPCD\n");
182 }
183
184 static void
intel_dp_aux_hdr_set_backlight(const struct drm_connector_state * conn_state,u32 level)185 intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level)
186 {
187 struct intel_connector *connector = to_intel_connector(conn_state->connector);
188 struct intel_panel *panel = &connector->panel;
189
190 if (panel->backlight.edp.intel.sdr_uses_aux) {
191 intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
192 } else {
193 const u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
194
195 intel_backlight_set_pwm_level(conn_state, pwm_level);
196 }
197 }
198
199 static void
intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state,u32 level)200 intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state,
201 const struct drm_connector_state *conn_state, u32 level)
202 {
203 struct intel_connector *connector = to_intel_connector(conn_state->connector);
204 struct intel_panel *panel = &connector->panel;
205 struct drm_i915_private *i915 = to_i915(connector->base.dev);
206 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
207 int ret;
208 u8 old_ctrl, ctrl;
209
210 intel_dp_wait_source_oui(intel_dp);
211
212 ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl);
213 if (ret != 1) {
214 drm_err(&i915->drm, "Failed to read current backlight control mode: %d\n", ret);
215 return;
216 }
217
218 ctrl = old_ctrl;
219 if (panel->backlight.edp.intel.sdr_uses_aux) {
220 ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
221 intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
222 } else {
223 u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
224
225 panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
226
227 ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
228 }
229
230 if (ctrl != old_ctrl)
231 if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) != 1)
232 drm_err(&i915->drm, "Failed to configure DPCD brightness controls\n");
233 }
234
235 static void
intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state * conn_state,u32 level)236 intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
237 {
238 struct intel_connector *connector = to_intel_connector(conn_state->connector);
239 struct intel_panel *panel = &connector->panel;
240
241 /* Nothing to do for AUX based backlight controls */
242 if (panel->backlight.edp.intel.sdr_uses_aux)
243 return;
244
245 /* Note we want the actual pwm_level to be 0, regardless of pwm_min */
246 panel->backlight.pwm_funcs->disable(conn_state, intel_backlight_invert_pwm_level(connector, 0));
247 }
248
249 static int
intel_dp_aux_hdr_setup_backlight(struct intel_connector * connector,enum pipe pipe)250 intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe)
251 {
252 struct drm_i915_private *i915 = to_i915(connector->base.dev);
253 struct intel_panel *panel = &connector->panel;
254 int ret;
255
256 if (panel->backlight.edp.intel.sdr_uses_aux) {
257 drm_dbg_kms(&i915->drm, "SDR backlight is controlled through DPCD\n");
258 } else {
259 drm_dbg_kms(&i915->drm, "SDR backlight is controlled through PWM\n");
260
261 ret = panel->backlight.pwm_funcs->setup(connector, pipe);
262 if (ret < 0) {
263 drm_err(&i915->drm,
264 "Failed to setup SDR backlight controls through PWM: %d\n", ret);
265 return ret;
266 }
267 }
268
269 panel->backlight.max = 512;
270 panel->backlight.min = 0;
271 panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe);
272 panel->backlight.enabled = panel->backlight.level != 0;
273
274 return 0;
275 }
276
277 /* VESA backlight callbacks */
intel_dp_aux_vesa_get_backlight(struct intel_connector * connector,enum pipe unused)278 static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused)
279 {
280 return connector->panel.backlight.level;
281 }
282
283 static void
intel_dp_aux_vesa_set_backlight(const struct drm_connector_state * conn_state,u32 level)284 intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level)
285 {
286 struct intel_connector *connector = to_intel_connector(conn_state->connector);
287 struct intel_panel *panel = &connector->panel;
288 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
289
290 drm_edp_backlight_set_level(&intel_dp->aux, &panel->backlight.edp.vesa.info, level);
291 }
292
293 static void
intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state,u32 level)294 intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
295 const struct drm_connector_state *conn_state, u32 level)
296 {
297 struct intel_connector *connector = to_intel_connector(conn_state->connector);
298 struct intel_panel *panel = &connector->panel;
299 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
300
301 if (!panel->backlight.edp.vesa.info.aux_enable) {
302 u32 pwm_level = intel_backlight_invert_pwm_level(connector,
303 panel->backlight.pwm_level_max);
304
305 panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
306 }
307
308 drm_edp_backlight_enable(&intel_dp->aux, &panel->backlight.edp.vesa.info, level);
309 }
310
intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state * old_conn_state,u32 level)311 static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state,
312 u32 level)
313 {
314 struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
315 struct intel_panel *panel = &connector->panel;
316 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
317
318 drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info);
319
320 if (!panel->backlight.edp.vesa.info.aux_enable)
321 panel->backlight.pwm_funcs->disable(old_conn_state,
322 intel_backlight_invert_pwm_level(connector, 0));
323 }
324
intel_dp_aux_vesa_setup_backlight(struct intel_connector * connector,enum pipe pipe)325 static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe)
326 {
327 struct intel_dp *intel_dp = intel_attached_dp(connector);
328 struct intel_panel *panel = &connector->panel;
329 struct drm_i915_private *i915 = dp_to_i915(intel_dp);
330 u16 current_level;
331 u8 current_mode;
332 int ret;
333
334 ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info,
335 i915->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd,
336 ¤t_level, ¤t_mode);
337 if (ret < 0)
338 return ret;
339
340 if (!panel->backlight.edp.vesa.info.aux_enable) {
341 ret = panel->backlight.pwm_funcs->setup(connector, pipe);
342 if (ret < 0) {
343 drm_err(&i915->drm,
344 "Failed to setup PWM backlight controls for eDP backlight: %d\n",
345 ret);
346 return ret;
347 }
348 }
349 panel->backlight.max = panel->backlight.edp.vesa.info.max;
350 panel->backlight.min = 0;
351 if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
352 panel->backlight.level = current_level;
353 panel->backlight.enabled = panel->backlight.level != 0;
354 } else {
355 panel->backlight.level = panel->backlight.max;
356 panel->backlight.enabled = false;
357 }
358
359 return 0;
360 }
361
362 static bool
intel_dp_aux_supports_vesa_backlight(struct intel_connector * connector)363 intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector)
364 {
365 struct intel_dp *intel_dp = intel_attached_dp(connector);
366 struct drm_i915_private *i915 = dp_to_i915(intel_dp);
367
368 if (drm_edp_backlight_supported(intel_dp->edp_dpcd)) {
369 drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n");
370 return true;
371 }
372 return false;
373 }
374
375 static const struct intel_panel_bl_funcs intel_dp_hdr_bl_funcs = {
376 .setup = intel_dp_aux_hdr_setup_backlight,
377 .enable = intel_dp_aux_hdr_enable_backlight,
378 .disable = intel_dp_aux_hdr_disable_backlight,
379 .set = intel_dp_aux_hdr_set_backlight,
380 .get = intel_dp_aux_hdr_get_backlight,
381 };
382
383 static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = {
384 .setup = intel_dp_aux_vesa_setup_backlight,
385 .enable = intel_dp_aux_vesa_enable_backlight,
386 .disable = intel_dp_aux_vesa_disable_backlight,
387 .set = intel_dp_aux_vesa_set_backlight,
388 .get = intel_dp_aux_vesa_get_backlight,
389 };
390
391 enum intel_dp_aux_backlight_modparam {
392 INTEL_DP_AUX_BACKLIGHT_AUTO = -1,
393 INTEL_DP_AUX_BACKLIGHT_OFF = 0,
394 INTEL_DP_AUX_BACKLIGHT_ON = 1,
395 INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2,
396 INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3,
397 };
398
intel_dp_aux_init_backlight_funcs(struct intel_connector * connector)399 int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
400 {
401 struct drm_device *dev = connector->base.dev;
402 struct intel_panel *panel = &connector->panel;
403 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
404 struct drm_i915_private *i915 = dp_to_i915(intel_dp);
405 bool try_intel_interface = false, try_vesa_interface = false;
406
407 /* Check the VBT and user's module parameters to figure out which
408 * interfaces to probe
409 */
410 switch (i915->params.enable_dpcd_backlight) {
411 case INTEL_DP_AUX_BACKLIGHT_OFF:
412 return -ENODEV;
413 case INTEL_DP_AUX_BACKLIGHT_AUTO:
414 switch (i915->vbt.backlight.type) {
415 case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE:
416 try_vesa_interface = true;
417 break;
418 case INTEL_BACKLIGHT_DISPLAY_DDI:
419 try_intel_interface = true;
420 break;
421 default:
422 return -ENODEV;
423 }
424 break;
425 case INTEL_DP_AUX_BACKLIGHT_ON:
426 if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
427 try_intel_interface = true;
428
429 try_vesa_interface = true;
430 break;
431 case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA:
432 try_vesa_interface = true;
433 break;
434 case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL:
435 try_intel_interface = true;
436 break;
437 }
438
439 /*
440 * A lot of eDP panels in the wild will report supporting both the
441 * Intel proprietary backlight control interface, and the VESA
442 * backlight control interface. Many of these panels are liars though,
443 * and will only work with the Intel interface. So, always probe for
444 * that first.
445 */
446 if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) {
447 drm_dbg_kms(dev, "Using Intel proprietary eDP backlight controls\n");
448 panel->backlight.funcs = &intel_dp_hdr_bl_funcs;
449 return 0;
450 }
451
452 if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) {
453 drm_dbg_kms(dev, "Using VESA eDP backlight controls\n");
454 panel->backlight.funcs = &intel_dp_vesa_bl_funcs;
455 return 0;
456 }
457
458 return -ENODEV;
459 }
460