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