File Index
Files and directories
Sign in
FILE

intel_dp_aux_backlight.c

File preview with syntax highlighting.

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