xref: /linux/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
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 				     &current_level, &current_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