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