Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 95enum 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
103static bool is_intel_tcon_cap(const u8 tcon_cap[4])
104{
105	return tcon_cap[0] >= 1;
106}
107
108/* Intel EDP backlight callbacks */
109static bool
110intel_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
171static u32
172intel_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
209static void
210intel_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
226static bool
227intel_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
239static void
240intel_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
255static void
256intel_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
281static void
282intel_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
313static void
314intel_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
363static void
364intel_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
377static const char *dpcd_vs_pwm_str(bool aux)
378{
379	return aux ? "DPCD" : "PWM";
380}
381
382static void
383intel_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
405static int
406intel_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 */
451static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused)
452{
453	return connector->panel.backlight.level;
454}
455
456static void
457intel_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
472static void
473intel_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
495static 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
509static 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
572static bool
573intel_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
587static 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
595static 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
603int 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}