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 "i915_utils.h"
38 #include "intel_backlight.h"
39 #include "intel_display_core.h"
40 #include "intel_display_types.h"
41 #include "intel_dp.h"
42 #include "intel_dp_aux_backlight.h"
43
44 /*
45 * DP AUX registers for Intel's proprietary HDR backlight interface. We define
46 * them here since we'll likely be the only driver to ever use these.
47 */
48 #define INTEL_EDP_HDR_TCON_CAP0 0x340
49
50 #define INTEL_EDP_HDR_TCON_CAP1 0x341
51 # define INTEL_EDP_HDR_TCON_2084_DECODE_CAP BIT(0)
52 # define INTEL_EDP_HDR_TCON_2020_GAMUT_CAP BIT(1)
53 # define INTEL_EDP_HDR_TCON_TONE_MAPPING_CAP BIT(2)
54 # define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_CAP BIT(3)
55 # define INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP BIT(4)
56 # define INTEL_EDP_HDR_TCON_OPTIMIZATION_CAP BIT(5)
57 # define INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_CAP BIT(6)
58 # define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_CONVERSION_CAP BIT(7)
59
60 #define INTEL_EDP_HDR_TCON_CAP2 0x342
61 # define INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP BIT(0)
62
63 #define INTEL_EDP_HDR_TCON_CAP3 0x343
64
65 #define INTEL_EDP_HDR_GETSET_CTRL_PARAMS 0x344
66 # define INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE BIT(0)
67 # define INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE BIT(1)
68 # define INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE BIT(2)
69 # define INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE BIT(3)
70 # define INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE BIT(4)
71 # define INTEL_EDP_HDR_TCON_SRGB_TO_PANEL_GAMUT_ENABLE BIT(5)
72 /* Bit 6 is reserved */
73 # define INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX BIT(7)
74
75 #define INTEL_EDP_HDR_CONTENT_LUMINANCE 0x346
76 #define INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE 0x34A
77 #define INTEL_EDP_SDR_LUMINANCE_LEVEL 0x352
78 #define INTEL_EDP_BRIGHTNESS_NITS_LSB 0x354
79 #define INTEL_EDP_BRIGHTNESS_NITS_MSB 0x355
80 #define INTEL_EDP_BRIGHTNESS_DELAY_FRAMES 0x356
81 #define INTEL_EDP_BRIGHTNESS_PER_FRAME_STEPS 0x357
82
83 #define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_0 0x358
84 # define INTEL_EDP_TCON_USAGE_MASK GENMASK(0, 3)
85 # define INTEL_EDP_TCON_USAGE_UNKNOWN 0x0
86 # define INTEL_EDP_TCON_USAGE_DESKTOP 0x1
87 # define INTEL_EDP_TCON_USAGE_FULL_SCREEN_MEDIA 0x2
88 # define INTEL_EDP_TCON_USAGE_FULL_SCREEN_GAMING 0x3
89 # define INTEL_EDP_TCON_POWER_MASK BIT(4)
90 # define INTEL_EDP_TCON_POWER_DC (0 << 4)
91 # define INTEL_EDP_TCON_POWER_AC (1 << 4)
92 # define INTEL_EDP_TCON_OPTIMIZATION_STRENGTH_MASK GENMASK(5, 7)
93
94 #define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_1 0x359
95
96 enum intel_dp_aux_backlight_modparam {
97 INTEL_DP_AUX_BACKLIGHT_AUTO = -1,
98 INTEL_DP_AUX_BACKLIGHT_OFF = 0,
99 INTEL_DP_AUX_BACKLIGHT_ON = 1,
100 INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2,
101 INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3,
102 };
103
is_intel_tcon_cap(const u8 tcon_cap[4])104 static bool is_intel_tcon_cap(const u8 tcon_cap[4])
105 {
106 return tcon_cap[0] >= 1;
107 }
108
109 /* Intel EDP backlight callbacks */
110 static bool
intel_dp_aux_supports_hdr_backlight(struct intel_connector * connector)111 intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector)
112 {
113 struct intel_display *display = to_intel_display(connector);
114 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
115 struct drm_dp_aux *aux = &intel_dp->aux;
116 struct intel_panel *panel = &connector->panel;
117 int ret;
118 u8 tcon_cap[4];
119
120 intel_dp_wait_source_oui(intel_dp);
121
122 ret = drm_dp_dpcd_read(aux, INTEL_EDP_HDR_TCON_CAP0, tcon_cap, sizeof(tcon_cap));
123 if (ret != sizeof(tcon_cap))
124 return false;
125
126 drm_dbg_kms(display->drm,
127 "[CONNECTOR:%d:%s] Detected %s HDR backlight interface version %d\n",
128 connector->base.base.id, connector->base.name,
129 is_intel_tcon_cap(tcon_cap) ? "Intel" : "unsupported", tcon_cap[0]);
130
131 if (!is_intel_tcon_cap(tcon_cap))
132 return false;
133
134 if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP))
135 return false;
136
137 /*
138 * If we don't have HDR static metadata there is no way to
139 * runtime detect used range for nits based control. For now
140 * do not use Intel proprietary eDP backlight control if we
141 * don't have this data in panel EDID. In case we find panel
142 * which supports only nits based control, but doesn't provide
143 * HDR static metadata we need to start maintaining table of
144 * ranges for such panels.
145 */
146 if (display->params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL &&
147 !(connector->base.hdr_sink_metadata.hdmi_type1.metadata_type &
148 BIT(HDMI_STATIC_METADATA_TYPE1))) {
149 drm_info(display->drm,
150 "[CONNECTOR:%d:%s] Panel is missing HDR static metadata. Possible support for Intel HDR backlight interface is not used. If your backlight controls don't work try booting with i915.enable_dpcd_backlight=%d. needs this, please file a _new_ bug report on drm/i915, see " FDO_BUG_URL " for details.\n",
151 connector->base.base.id, connector->base.name,
152 INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL);
153 return false;
154 }
155
156 panel->backlight.edp.intel_cap.sdr_uses_aux =
157 tcon_cap[2] & INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP;
158 panel->backlight.edp.intel_cap.supports_2084_decode =
159 tcon_cap[1] & INTEL_EDP_HDR_TCON_2084_DECODE_CAP;
160 panel->backlight.edp.intel_cap.supports_2020_gamut =
161 tcon_cap[1] & INTEL_EDP_HDR_TCON_2020_GAMUT_CAP;
162 panel->backlight.edp.intel_cap.supports_segmented_backlight =
163 tcon_cap[1] & INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_CAP;
164 panel->backlight.edp.intel_cap.supports_sdp_colorimetry =
165 tcon_cap[1] & INTEL_EDP_HDR_TCON_SDP_COLORIMETRY_CAP;
166 panel->backlight.edp.intel_cap.supports_tone_mapping =
167 tcon_cap[1] & INTEL_EDP_HDR_TCON_TONE_MAPPING_CAP;
168
169 return true;
170 }
171
172 static u32
intel_dp_aux_hdr_get_backlight(struct intel_connector * connector,enum pipe pipe)173 intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe)
174 {
175 struct intel_display *display = to_intel_display(connector);
176 struct intel_panel *panel = &connector->panel;
177 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
178 u8 tmp;
179 u8 buf[2] = {};
180
181 if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) != 1) {
182 drm_err(display->drm,
183 "[CONNECTOR:%d:%s] Failed to read current backlight mode from DPCD\n",
184 connector->base.base.id, connector->base.name);
185 return 0;
186 }
187
188 if (!(tmp & INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE)) {
189 if (!panel->backlight.edp.intel_cap.sdr_uses_aux) {
190 u32 pwm_level = panel->backlight.pwm_funcs->get(connector, pipe);
191
192 return intel_backlight_level_from_pwm(connector, pwm_level);
193 }
194
195 /* Assume 100% brightness if backlight controls aren't enabled yet */
196 return panel->backlight.max;
197 }
198
199 if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf,
200 sizeof(buf)) != sizeof(buf)) {
201 drm_err(display->drm,
202 "[CONNECTOR:%d:%s] Failed to read brightness from DPCD\n",
203 connector->base.base.id, connector->base.name);
204 return 0;
205 }
206
207 return (buf[1] << 8 | buf[0]);
208 }
209
210 static void
intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state * conn_state,u32 level)211 intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, u32 level)
212 {
213 struct intel_connector *connector = to_intel_connector(conn_state->connector);
214 struct drm_device *dev = connector->base.dev;
215 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
216 u8 buf[4] = {};
217
218 buf[0] = level & 0xFF;
219 buf[1] = (level & 0xFF00) >> 8;
220
221 if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf,
222 sizeof(buf)) != sizeof(buf))
223 drm_err(dev, "[CONNECTOR:%d:%s] Failed to write brightness level to DPCD\n",
224 connector->base.base.id, connector->base.name);
225 }
226
227 static bool
intel_dp_in_hdr_mode(const struct drm_connector_state * conn_state)228 intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state)
229 {
230 struct hdr_output_metadata *hdr_metadata;
231
232 if (!conn_state->hdr_output_metadata)
233 return false;
234
235 hdr_metadata = conn_state->hdr_output_metadata->data;
236
237 return hdr_metadata->hdmi_metadata_type1.eotf == HDMI_EOTF_SMPTE_ST2084;
238 }
239
240 static void
intel_dp_aux_hdr_set_backlight(const struct drm_connector_state * conn_state,u32 level)241 intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level)
242 {
243 struct intel_connector *connector = to_intel_connector(conn_state->connector);
244 struct intel_panel *panel = &connector->panel;
245
246 if (intel_dp_in_hdr_mode(conn_state) ||
247 panel->backlight.edp.intel_cap.sdr_uses_aux) {
248 intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
249 } else {
250 const u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
251
252 intel_backlight_set_pwm_level(conn_state, pwm_level);
253 }
254 }
255
256 static void
intel_dp_aux_write_content_luminance(struct intel_connector * connector,struct hdr_output_metadata * hdr_metadata)257 intel_dp_aux_write_content_luminance(struct intel_connector *connector,
258 struct hdr_output_metadata *hdr_metadata)
259 {
260 struct intel_display *display = to_intel_display(connector);
261 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
262 int ret;
263 u8 buf[4];
264
265 if (!intel_dp_has_gamut_metadata_dip(connector->encoder))
266 return;
267
268 buf[0] = hdr_metadata->hdmi_metadata_type1.max_cll & 0xFF;
269 buf[1] = (hdr_metadata->hdmi_metadata_type1.max_cll & 0xFF00) >> 8;
270 buf[2] = hdr_metadata->hdmi_metadata_type1.max_fall & 0xFF;
271 buf[3] = (hdr_metadata->hdmi_metadata_type1.max_fall & 0xFF00) >> 8;
272
273 ret = drm_dp_dpcd_write(&intel_dp->aux,
274 INTEL_EDP_HDR_CONTENT_LUMINANCE,
275 buf, sizeof(buf));
276 if (ret < 0)
277 drm_dbg_kms(display->drm,
278 "Content Luminance DPCD reg write failed, err:-%d\n",
279 ret);
280 }
281
282 static void
intel_dp_aux_fill_hdr_tcon_params(const struct drm_connector_state * conn_state,u8 * ctrl)283 intel_dp_aux_fill_hdr_tcon_params(const struct drm_connector_state *conn_state, u8 *ctrl)
284 {
285 struct intel_connector *connector = to_intel_connector(conn_state->connector);
286 struct intel_panel *panel = &connector->panel;
287 struct intel_display *display = to_intel_display(connector);
288
289 /*
290 * According to spec segmented backlight needs to be set whenever panel is in
291 * HDR mode.
292 */
293 if (intel_dp_in_hdr_mode(conn_state)) {
294 *ctrl |= INTEL_EDP_HDR_TCON_SEGMENTED_BACKLIGHT_ENABLE;
295 *ctrl |= INTEL_EDP_HDR_TCON_2084_DECODE_ENABLE;
296 }
297
298 if (DISPLAY_VER(display) < 11)
299 *ctrl &= ~INTEL_EDP_HDR_TCON_TONE_MAPPING_ENABLE;
300
301 if (panel->backlight.edp.intel_cap.supports_2020_gamut &&
302 (conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_RGB ||
303 conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_YCC ||
304 conn_state->colorspace == DRM_MODE_COLORIMETRY_BT2020_CYCC))
305 *ctrl |= INTEL_EDP_HDR_TCON_2020_GAMUT_ENABLE;
306
307 if (panel->backlight.edp.intel_cap.supports_sdp_colorimetry &&
308 intel_dp_has_gamut_metadata_dip(connector->encoder))
309 *ctrl |= INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX;
310 else
311 *ctrl &= ~INTEL_EDP_HDR_TCON_SDP_OVERRIDE_AUX;
312 }
313
314 static void
intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state,u32 level)315 intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state,
316 const struct drm_connector_state *conn_state, u32 level)
317 {
318 struct intel_display *display = to_intel_display(crtc_state);
319 struct intel_connector *connector = to_intel_connector(conn_state->connector);
320 struct intel_panel *panel = &connector->panel;
321 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
322 struct hdr_output_metadata *hdr_metadata;
323 int ret;
324 u8 old_ctrl, ctrl;
325
326 intel_dp_wait_source_oui(intel_dp);
327
328 ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl);
329 if (ret != 1) {
330 drm_err(display->drm,
331 "[CONNECTOR:%d:%s] Failed to read current backlight control mode: %d\n",
332 connector->base.base.id, connector->base.name, ret);
333 return;
334 }
335
336 ctrl = old_ctrl;
337 if (intel_dp_in_hdr_mode(conn_state) ||
338 panel->backlight.edp.intel_cap.sdr_uses_aux) {
339 ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
340
341 intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
342 } else {
343 u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
344
345 panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
346
347 ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
348 }
349
350 intel_dp_aux_fill_hdr_tcon_params(conn_state, &ctrl);
351
352 if (ctrl != old_ctrl &&
353 drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) != 1)
354 drm_err(display->drm,
355 "[CONNECTOR:%d:%s] Failed to configure DPCD brightness controls\n",
356 connector->base.base.id, connector->base.name);
357
358 if (intel_dp_in_hdr_mode(conn_state)) {
359 hdr_metadata = conn_state->hdr_output_metadata->data;
360 intel_dp_aux_write_content_luminance(connector, hdr_metadata);
361 }
362 }
363
364 static void
intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state * conn_state,u32 level)365 intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
366 {
367 struct intel_connector *connector = to_intel_connector(conn_state->connector);
368 struct intel_panel *panel = &connector->panel;
369
370 /* Nothing to do for AUX based backlight controls */
371 if (panel->backlight.edp.intel_cap.sdr_uses_aux)
372 return;
373
374 /* Note we want the actual pwm_level to be 0, regardless of pwm_min */
375 panel->backlight.pwm_funcs->disable(conn_state, intel_backlight_invert_pwm_level(connector, 0));
376 }
377
dpcd_vs_pwm_str(bool aux)378 static const char *dpcd_vs_pwm_str(bool aux)
379 {
380 return aux ? "DPCD" : "PWM";
381 }
382
383 static void
intel_dp_aux_write_panel_luminance_override(struct intel_connector * connector)384 intel_dp_aux_write_panel_luminance_override(struct intel_connector *connector)
385 {
386 struct intel_display *display = to_intel_display(connector);
387 struct intel_panel *panel = &connector->panel;
388 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
389 int ret;
390 u8 buf[4] = {};
391
392 buf[0] = panel->backlight.min & 0xFF;
393 buf[1] = (panel->backlight.min & 0xFF00) >> 8;
394 buf[2] = panel->backlight.max & 0xFF;
395 buf[3] = (panel->backlight.max & 0xFF00) >> 8;
396
397 ret = drm_dp_dpcd_write(&intel_dp->aux,
398 INTEL_EDP_HDR_PANEL_LUMINANCE_OVERRIDE,
399 buf, sizeof(buf));
400 if (ret < 0)
401 drm_dbg_kms(display->drm,
402 "Panel Luminance DPCD reg write failed, err:-%d\n",
403 ret);
404 }
405
406 static int
intel_dp_aux_hdr_setup_backlight(struct intel_connector * connector,enum pipe pipe)407 intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe)
408 {
409 struct intel_display *display = to_intel_display(connector);
410 struct intel_panel *panel = &connector->panel;
411 struct drm_luminance_range_info *luminance_range =
412 &connector->base.display_info.luminance_range;
413 int ret;
414
415 drm_dbg_kms(display->drm,
416 "[CONNECTOR:%d:%s] SDR backlight is controlled through %s\n",
417 connector->base.base.id, connector->base.name,
418 dpcd_vs_pwm_str(panel->backlight.edp.intel_cap.sdr_uses_aux));
419
420 if (!panel->backlight.edp.intel_cap.sdr_uses_aux) {
421 ret = panel->backlight.pwm_funcs->setup(connector, pipe);
422 if (ret < 0) {
423 drm_err(display->drm,
424 "[CONNECTOR:%d:%s] Failed to setup SDR backlight controls through PWM: %d\n",
425 connector->base.base.id, connector->base.name, ret);
426 return ret;
427 }
428 }
429
430 if (luminance_range->max_luminance) {
431 panel->backlight.max = luminance_range->max_luminance;
432 panel->backlight.min = luminance_range->min_luminance;
433 } else {
434 panel->backlight.max = 512;
435 panel->backlight.min = 0;
436 }
437
438 intel_dp_aux_write_panel_luminance_override(connector);
439
440 drm_dbg_kms(display->drm,
441 "[CONNECTOR:%d:%s] Using AUX HDR interface for backlight control (range %d..%d)\n",
442 connector->base.base.id, connector->base.name,
443 panel->backlight.min, panel->backlight.max);
444
445 panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe);
446 panel->backlight.enabled = panel->backlight.level != 0;
447
448 return 0;
449 }
450
451 /* VESA backlight callbacks */
intel_dp_aux_vesa_get_backlight(struct intel_connector * connector,enum pipe unused)452 static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused)
453 {
454 return connector->panel.backlight.level;
455 }
456
457 static void
intel_dp_aux_vesa_set_backlight(const struct drm_connector_state * conn_state,u32 level)458 intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level)
459 {
460 struct intel_connector *connector = to_intel_connector(conn_state->connector);
461 struct intel_panel *panel = &connector->panel;
462 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
463
464 if (!panel->backlight.edp.vesa.info.aux_set) {
465 const u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
466
467 intel_backlight_set_pwm_level(conn_state, pwm_level);
468 }
469
470 drm_edp_backlight_set_level(&intel_dp->aux, &panel->backlight.edp.vesa.info, level);
471 }
472
473 static void
intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state,u32 level)474 intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
475 const struct drm_connector_state *conn_state, u32 level)
476 {
477 struct intel_connector *connector = to_intel_connector(conn_state->connector);
478 struct intel_panel *panel = &connector->panel;
479 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
480
481 if (!panel->backlight.edp.vesa.info.aux_enable) {
482 u32 pwm_level;
483
484 if (!panel->backlight.edp.vesa.info.aux_set)
485 pwm_level = intel_backlight_level_to_pwm(connector, level);
486 else
487 pwm_level = intel_backlight_invert_pwm_level(connector,
488 panel->backlight.pwm_level_max);
489
490 panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
491 }
492
493 drm_edp_backlight_enable(&intel_dp->aux, &panel->backlight.edp.vesa.info, level);
494 }
495
intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state * old_conn_state,u32 level)496 static void intel_dp_aux_vesa_disable_backlight(const struct drm_connector_state *old_conn_state,
497 u32 level)
498 {
499 struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
500 struct intel_panel *panel = &connector->panel;
501 struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
502
503 drm_edp_backlight_disable(&intel_dp->aux, &panel->backlight.edp.vesa.info);
504
505 if (!panel->backlight.edp.vesa.info.aux_enable)
506 panel->backlight.pwm_funcs->disable(old_conn_state,
507 intel_backlight_invert_pwm_level(connector, 0));
508 }
509
intel_dp_aux_vesa_setup_backlight(struct intel_connector * connector,enum pipe pipe)510 static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, enum pipe pipe)
511 {
512 struct intel_display *display = to_intel_display(connector);
513 struct intel_dp *intel_dp = intel_attached_dp(connector);
514 struct intel_panel *panel = &connector->panel;
515 u16 current_level;
516 u8 current_mode;
517 int ret;
518
519 ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info,
520 panel->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd,
521 ¤t_level, ¤t_mode);
522 if (ret < 0)
523 return ret;
524
525 drm_dbg_kms(display->drm,
526 "[CONNECTOR:%d:%s] AUX VESA backlight enable is controlled through %s\n",
527 connector->base.base.id, connector->base.name,
528 dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_enable));
529 drm_dbg_kms(display->drm,
530 "[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n",
531 connector->base.base.id, connector->base.name,
532 dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set));
533
534 if (!panel->backlight.edp.vesa.info.aux_set || !panel->backlight.edp.vesa.info.aux_enable) {
535 ret = panel->backlight.pwm_funcs->setup(connector, pipe);
536 if (ret < 0) {
537 drm_err(display->drm,
538 "[CONNECTOR:%d:%s] Failed to setup PWM backlight controls for eDP backlight: %d\n",
539 connector->base.base.id, connector->base.name, ret);
540 return ret;
541 }
542 }
543
544 if (panel->backlight.edp.vesa.info.aux_set) {
545 panel->backlight.max = panel->backlight.edp.vesa.info.max;
546 panel->backlight.min = 0;
547 if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
548 panel->backlight.level = current_level;
549 panel->backlight.enabled = panel->backlight.level != 0;
550 } else {
551 panel->backlight.level = panel->backlight.max;
552 panel->backlight.enabled = false;
553 }
554 } else {
555 panel->backlight.max = panel->backlight.pwm_level_max;
556 panel->backlight.min = panel->backlight.pwm_level_min;
557 if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_PWM) {
558 panel->backlight.level = panel->backlight.pwm_funcs->get(connector, pipe);
559 panel->backlight.enabled = panel->backlight.pwm_enabled;
560 } else {
561 panel->backlight.level = panel->backlight.max;
562 panel->backlight.enabled = false;
563 }
564 }
565
566 drm_dbg_kms(display->drm,
567 "[CONNECTOR:%d:%s] Using AUX VESA interface for backlight control\n",
568 connector->base.base.id, connector->base.name);
569
570 return 0;
571 }
572
573 static bool
intel_dp_aux_supports_vesa_backlight(struct intel_connector * connector)574 intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector)
575 {
576 struct intel_display *display = to_intel_display(connector);
577 struct intel_dp *intel_dp = intel_attached_dp(connector);
578
579 if (drm_edp_backlight_supported(intel_dp->edp_dpcd)) {
580 drm_dbg_kms(display->drm,
581 "[CONNECTOR:%d:%s] AUX Backlight Control Supported!\n",
582 connector->base.base.id, connector->base.name);
583 return true;
584 }
585 return false;
586 }
587
588 static const struct intel_panel_bl_funcs intel_dp_hdr_bl_funcs = {
589 .setup = intel_dp_aux_hdr_setup_backlight,
590 .enable = intel_dp_aux_hdr_enable_backlight,
591 .disable = intel_dp_aux_hdr_disable_backlight,
592 .set = intel_dp_aux_hdr_set_backlight,
593 .get = intel_dp_aux_hdr_get_backlight,
594 };
595
596 static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = {
597 .setup = intel_dp_aux_vesa_setup_backlight,
598 .enable = intel_dp_aux_vesa_enable_backlight,
599 .disable = intel_dp_aux_vesa_disable_backlight,
600 .set = intel_dp_aux_vesa_set_backlight,
601 .get = intel_dp_aux_vesa_get_backlight,
602 };
603
intel_dp_aux_init_backlight_funcs(struct intel_connector * connector)604 int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
605 {
606 struct intel_display *display = to_intel_display(connector);
607 struct drm_device *dev = connector->base.dev;
608 struct intel_panel *panel = &connector->panel;
609 bool try_intel_interface = false, try_vesa_interface = false;
610
611 /* Check the VBT and user's module parameters to figure out which
612 * interfaces to probe
613 */
614 switch (display->params.enable_dpcd_backlight) {
615 case INTEL_DP_AUX_BACKLIGHT_OFF:
616 return -ENODEV;
617 case INTEL_DP_AUX_BACKLIGHT_AUTO:
618 switch (panel->vbt.backlight.type) {
619 case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE:
620 try_vesa_interface = true;
621 break;
622 case INTEL_BACKLIGHT_DISPLAY_DDI:
623 try_intel_interface = true;
624 break;
625 default:
626 return -ENODEV;
627 }
628 break;
629 case INTEL_DP_AUX_BACKLIGHT_ON:
630 if (panel->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
631 try_intel_interface = true;
632
633 try_vesa_interface = true;
634 break;
635 case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA:
636 try_vesa_interface = true;
637 break;
638 case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL:
639 try_intel_interface = true;
640 break;
641 }
642
643 /*
644 * Since Intel has their own backlight control interface, the majority of machines out there
645 * using DPCD backlight controls with Intel GPUs will be using this interface as opposed to
646 * the VESA interface. However, other GPUs (such as Nvidia's) will always use the VESA
647 * interface. This means that there's quite a number of panels out there that will advertise
648 * support for both interfaces, primarily systems with Intel/Nvidia hybrid GPU setups.
649 *
650 * There's a catch to this though: on many panels that advertise support for both
651 * interfaces, the VESA backlight interface will stop working once we've programmed the
652 * panel with Intel's OUI - which is also required for us to be able to detect Intel's
653 * backlight interface at all. This means that the only sensible way for us to detect both
654 * interfaces is to probe for Intel's first, and VESA's second.
655 */
656 if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) {
657 drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using Intel proprietary eDP backlight controls\n",
658 connector->base.base.id, connector->base.name);
659 panel->backlight.funcs = &intel_dp_hdr_bl_funcs;
660 return 0;
661 }
662
663 if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) {
664 drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Using VESA eDP backlight controls\n",
665 connector->base.base.id, connector->base.name);
666 panel->backlight.funcs = &intel_dp_vesa_bl_funcs;
667 return 0;
668 }
669
670 return -ENODEV;
671 }
672