xref: /linux/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c (revision 654ae5d73c05bd2943d65636ce6cd0aa46e62f18)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright 2026 Advanced Micro Devices, Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors: AMD
24  */
25 
26 #include "dc.h"
27 #include "dc/dc_dmub_srv.h"
28 #include "dc/dc_state.h"
29 #include "dc/dc_stat.h"
30 
31 #include "amdgpu.h"
32 #include "amdgpu_display.h"
33 #include "amdgpu_dm.h"
34 #include "amdgpu_dm_backlight.h"
35 #include "amdgpu_dm_psr.h"
36 #include "amdgpu_dm_replay.h"
37 #include "amdgpu_atombios.h"
38 
39 #include "modules/inc/mod_power.h"
40 
41 #include <linux/backlight.h>
42 #include <linux/power_supply.h>
43 #include <drm/drm_edid.h>
44 #include <drm/drm_utils.h>
45 
46 #include <acpi/video.h>
47 
48 #include "amdgpu_dm_trace.h"
49 #include "amd_shared.h"
50 #include "dm_helpers.h"
51 
52 void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
53 				     int bl_idx)
54 {
55 	struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
56 
57 	if (caps->caps_valid)
58 		return;
59 
60 #if defined(CONFIG_ACPI)
61 	amdgpu_acpi_get_backlight_caps(caps);
62 
63 	/* validate the firmware value is sane */
64 	if (caps->caps_valid) {
65 		int spread = caps->max_input_signal - caps->min_input_signal;
66 
67 		if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
68 		    caps->min_input_signal < 0 ||
69 		    spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
70 		    spread < AMDGPU_DM_MIN_SPREAD) {
71 			drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n",
72 				      caps->min_input_signal, caps->max_input_signal);
73 			caps->caps_valid = false;
74 		}
75 	}
76 #else
77 	if (caps->aux_support)
78 		return;
79 #endif
80 	if (!caps->caps_valid) {
81 		caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
82 		caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
83 		caps->ac_level = caps->dc_level = 50;
84 		caps->caps_valid = true;
85 	}
86 }
87 EXPORT_IF_KUNIT(amdgpu_dm_update_backlight_caps);
88 
89 STATIC_IFN_KUNIT
90 int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
91 			 unsigned int *min, unsigned int *max)
92 {
93 	if (!caps)
94 		return 0;
95 
96 	if (caps->aux_support) {
97 		/* Firmware limits are in nits, DC API wants millinits. */
98 		*max = 1000 * caps->aux_max_input_signal;
99 		*min = 1000 * caps->aux_min_input_signal;
100 	} else {
101 		/* Firmware limits are 8-bit, PWM control is 16-bit. */
102 		*max = 0x101 * caps->max_input_signal;
103 		*min = 0x101 * caps->min_input_signal;
104 	}
105 	return 1;
106 }
107 EXPORT_IF_KUNIT(get_brightness_range);
108 
109 /* Rescale userspace [0..max] to the firmware curve's [0..255]. */
110 static inline u32 scale_input_to_fw(int max, u64 input)
111 {
112 	return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max);
113 }
114 
115 /* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
116 static inline u32 scale_fw_to_input(int min, int max, u64 input)
117 {
118 	return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
119 }
120 
121 STATIC_IFN_KUNIT
122 void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
123 			       unsigned int min, unsigned int max,
124 			       uint32_t *user_brightness)
125 {
126 	u32 brightness = scale_input_to_fw(max, *user_brightness);
127 	u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
128 	int left, right;
129 
130 	if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
131 		return;
132 
133 	if (!caps->data_points)
134 		return;
135 
136 	/*
137 	 * Handle the case where brightness is below the first data point
138 	 * Interpolate between (0,0) and (first_signal, first_lum)
139 	 */
140 	if (brightness < caps->luminance_data[0].input_signal) {
141 		lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness,
142 					caps->luminance_data[0].input_signal);
143 		goto scale;
144 	}
145 
146 	left = 0;
147 	right = caps->data_points - 1;
148 	while (left <= right) {
149 		int mid = left + (right - left) / 2;
150 		u8 signal = caps->luminance_data[mid].input_signal;
151 
152 		/* Exact match found */
153 		if (signal == brightness) {
154 			lum = caps->luminance_data[mid].luminance;
155 			goto scale;
156 		}
157 
158 		if (signal < brightness)
159 			left = mid + 1;
160 		else
161 			right = mid - 1;
162 	}
163 
164 	/* verify bound */
165 	if (left >= caps->data_points)
166 		left = caps->data_points - 1;
167 
168 	/* At this point, left > right */
169 	lower_signal = caps->luminance_data[right].input_signal;
170 	upper_signal = caps->luminance_data[left].input_signal;
171 	lower_lum = caps->luminance_data[right].luminance;
172 	upper_lum = caps->luminance_data[left].luminance;
173 
174 	/* interpolate */
175 	if (right == left || !lower_lum)
176 		lum = upper_lum;
177 	else
178 		lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
179 						    (brightness - lower_signal),
180 						    upper_signal - lower_signal);
181 scale:
182 	*user_brightness = scale_fw_to_input(min, max,
183 					     DIV_ROUND_CLOSEST(lum * brightness, 101));
184 }
185 
186 EXPORT_IF_KUNIT(convert_custom_brightness);
187 
188 STATIC_IFN_KUNIT
189 u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
190 				uint32_t brightness)
191 {
192 	unsigned int min, max;
193 
194 	if (!get_brightness_range(caps, &min, &max))
195 		return brightness;
196 
197 	convert_custom_brightness(caps, min, max, &brightness);
198 
199 	/* Rescale 0..max to min..max */
200 	return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
201 }
202 
203 EXPORT_IF_KUNIT(convert_brightness_from_user);
204 
205 STATIC_IFN_KUNIT
206 u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
207 			      uint32_t brightness)
208 {
209 	unsigned int min, max;
210 
211 	if (!get_brightness_range(caps, &min, &max))
212 		return brightness;
213 
214 	if (brightness < min)
215 		return 0;
216 	/* Rescale min..max to 0..max */
217 	return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min),
218 				 max - min);
219 }
220 EXPORT_IF_KUNIT(convert_brightness_to_user);
221 
222 STATIC_IFN_KUNIT
223 struct dc_stream_state *dm_find_stream_with_link(
224 	struct amdgpu_display_manager *dm,
225 	struct dc_link *link)
226 {
227 	struct dc_state *cur_dc_state = dm->dc->current_state;
228 	struct dc_stream_state *stream = NULL;
229 	int i;
230 
231 	for (i = 0; i < cur_dc_state->stream_count; i++) {
232 		stream = cur_dc_state->streams[i];
233 		if (stream->link == link)
234 			return stream;
235 	}
236 
237 	return NULL;
238 }
239 EXPORT_IF_KUNIT(dm_find_stream_with_link);
240 
241 STATIC_IFN_KUNIT
242 int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm,
243 					 struct backlight_device *bd)
244 {
245 	int i;
246 
247 	for (i = 0; i < dm->num_of_edps; i++) {
248 		if (bd == dm->backlight_dev[i])
249 			return i;
250 	}
251 
252 	return 0;
253 }
254 EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_device_index);
255 
256 void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
257 				   int bl_idx,
258 				   u32 user_brightness)
259 {
260 	struct amdgpu_dm_backlight_caps *caps;
261 	struct dc_link *link;
262 	u32 brightness = 0;
263 	bool rc = false, reallow_idle = false;
264 	struct drm_connector *connector;
265 	struct dc_stream_state *stream;
266 	unsigned int min, max;
267 
268 	list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
269 		struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
270 
271 		if (aconnector->bl_idx != bl_idx)
272 			continue;
273 
274 		/* if connector is off, save the brightness for next time it's on */
275 		if (!aconnector->base.encoder) {
276 			dm->brightness[bl_idx] = user_brightness;
277 			dm->actual_brightness[bl_idx] = 0;
278 			return;
279 		}
280 	}
281 
282 	amdgpu_dm_update_backlight_caps(dm, bl_idx);
283 	caps = &dm->backlight_caps[bl_idx];
284 
285 	dm->brightness[bl_idx] = user_brightness;
286 	/* update scratch register */
287 	if (bl_idx == 0)
288 		amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
289 	brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
290 	link = (struct dc_link *)dm->backlight_link[bl_idx];
291 
292 	/* Apply brightness quirk */
293 	if (caps->brightness_mask)
294 		brightness |= caps->brightness_mask;
295 
296 	if (trace_amdgpu_dm_brightness_enabled()) {
297 		trace_amdgpu_dm_brightness(__builtin_return_address(0),
298 					   user_brightness,
299 					   brightness,
300 					   caps->aux_support,
301 					   power_supply_is_system_supplied() > 0);
302 	}
303 
304 	stream = dm_find_stream_with_link(dm, link);
305 	if (!stream)
306 		return;
307 
308 	mutex_lock(&dm->dc_lock);
309 	if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
310 		dc_allow_idle_optimizations(dm->dc, false);
311 		reallow_idle = true;
312 	}
313 
314 	if (caps->aux_support) {
315 		rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness,
316 			AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
317 	} else {
318 		/* power module uses millipercent */
319 		get_brightness_range(caps, &min, &max);
320 		brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
321 		rc = mod_power_set_backlight_percent(dm->power_module, stream,
322 						     brightness, 0, false);
323 	}
324 
325 	/*
326 	 * Some kms clients create a ramped backlight transition effect
327 	 * by rapidly changing the backlight. Yet we must wait on dmcub
328 	 * fw to exit psr/replay before programming backlight. To
329 	 * prevent lag, keep disable psr/replay and let the next atomic
330 	 * flip clear the event.
331 	 *
332 	 * ToDo: use ISM to handle rapidly backlight change
333 	 *
334 	 * Rapidly backlight change is similar to rapidly cursor events,
335 	 * which is now handled by ISM. ISM can delay the event until system
336 	 * is really idle, so we may use ISM to handle backlight change as well.
337 	 */
338 	amdgpu_dm_psr_set_event(dm, stream, true,
339 		psr_event_hw_programming, true);
340 	amdgpu_dm_replay_set_event(dm, stream, true,
341 		replay_event_hw_programming, true);
342 
343 	if (dm->dc->caps.ips_support && reallow_idle)
344 		dc_allow_idle_optimizations(dm->dc, true);
345 
346 	mutex_unlock(&dm->dc_lock);
347 
348 	if (rc)
349 		dm->actual_brightness[bl_idx] = user_brightness;
350 }
351 EXPORT_IF_KUNIT(amdgpu_dm_backlight_set_level);
352 
353 STATIC_IFN_KUNIT
354 int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
355 {
356 	struct amdgpu_display_manager *dm = bl_get_data(bd);
357 	int i = amdgpu_dm_backlight_get_device_index(dm, bd);
358 
359 	amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
360 
361 	return 0;
362 }
363 EXPORT_IF_KUNIT(amdgpu_dm_backlight_update_status);
364 
365 STATIC_IFN_KUNIT
366 u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, int bl_idx)
367 {
368 	int ret;
369 	struct amdgpu_dm_backlight_caps caps;
370 	struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
371 
372 	amdgpu_dm_update_backlight_caps(dm, bl_idx);
373 	caps = dm->backlight_caps[bl_idx];
374 
375 	if (caps.aux_support) {
376 		u32 avg, peak;
377 
378 		if (!dc_link_get_backlight_level_nits(link, &avg, &peak))
379 			return dm->brightness[bl_idx];
380 		return convert_brightness_to_user(&caps, avg);
381 	}
382 
383 	ret = dc_link_get_backlight_level(link);
384 
385 	if (ret == DC_ERROR_UNEXPECTED)
386 		return dm->brightness[bl_idx];
387 
388 	return convert_brightness_to_user(&caps, ret);
389 }
390 EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_level);
391 
392 STATIC_IFN_KUNIT
393 int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
394 {
395 	struct amdgpu_display_manager *dm = bl_get_data(bd);
396 	int i = amdgpu_dm_backlight_get_device_index(dm, bd);
397 
398 	return amdgpu_dm_backlight_get_level(dm, i);
399 }
400 EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_brightness);
401 
402 static const struct backlight_ops amdgpu_dm_backlight_ops = {
403 	.options = BL_CORE_SUSPENDRESUME,
404 	.get_brightness = amdgpu_dm_backlight_get_brightness,
405 	.update_status	= amdgpu_dm_backlight_update_status,
406 };
407 
408 STATIC_IFN_KUNIT
409 void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps,
410 				    bool is_system_supplied,
411 				    bool custom_curve_enabled,
412 				    struct backlight_properties *props)
413 {
414 	unsigned int min, max;
415 
416 	if (get_brightness_range(caps, &min, &max)) {
417 		if (is_system_supplied)
418 			props->brightness = DIV_ROUND_CLOSEST(max * caps->ac_level,
419 							       100);
420 		else
421 			props->brightness = DIV_ROUND_CLOSEST(max * caps->dc_level,
422 							       100);
423 		props->max_brightness = max;
424 	} else {
425 		props->brightness = MAX_BACKLIGHT_LEVEL;
426 		props->max_brightness = MAX_BACKLIGHT_LEVEL;
427 	}
428 
429 	if (caps && caps->data_points && custom_curve_enabled)
430 		props->scale = BACKLIGHT_SCALE_NON_LINEAR;
431 	else
432 		props->scale = BACKLIGHT_SCALE_LINEAR;
433 	props->type = BACKLIGHT_RAW;
434 }
435 EXPORT_IF_KUNIT(amdgpu_dm_backlight_fill_props);
436 
437 void
438 amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
439 {
440 	struct drm_device *drm = aconnector->base.dev;
441 	struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
442 	struct backlight_properties props = { 0 };
443 	struct amdgpu_dm_backlight_caps *caps;
444 	char bl_name[16];
445 	int real_brightness;
446 	int init_brightness;
447 
448 	if (aconnector->bl_idx == -1)
449 		return;
450 
451 	if (!acpi_video_backlight_use_native()) {
452 		drm_info(drm, "Skipping amdgpu DM backlight registration\n");
453 		/* Try registering an ACPI video backlight device instead. */
454 		acpi_video_register_backlight();
455 		return;
456 	}
457 
458 	caps = &dm->backlight_caps[aconnector->bl_idx];
459 	amdgpu_dm_backlight_fill_props(caps, power_supply_is_system_supplied() > 0,
460 				       !(amdgpu_dc_debug_mask &
461 					 DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE),
462 				       &props);
463 	drm_dbg(drm, "Backlight caps: max_brightness: %d, ac %d, dc %d\n",
464 		props.max_brightness, caps->ac_level, caps->dc_level);
465 
466 	init_brightness = props.brightness;
467 
468 	if (props.scale == BACKLIGHT_SCALE_NON_LINEAR)
469 		drm_info(drm, "Using custom brightness curve\n");
470 
471 	snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
472 		 drm->primary->index + aconnector->bl_idx);
473 
474 	dm->backlight_dev[aconnector->bl_idx] =
475 		backlight_device_register(bl_name, aconnector->base.kdev, dm,
476 					  &amdgpu_dm_backlight_ops, &props);
477 	dm->brightness[aconnector->bl_idx] = props.brightness;
478 
479 	if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
480 		drm_err(drm, "DM: Backlight registration failed!\n");
481 		dm->backlight_dev[aconnector->bl_idx] = NULL;
482 	} else {
483 		/*
484 		 * dm->brightness[x] can be inconsistent just after startup until
485 		 * ops.get_brightness is called.
486 		 */
487 		real_brightness =
488 			amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
489 
490 		if (real_brightness != init_brightness) {
491 			dm->actual_brightness[aconnector->bl_idx] = real_brightness;
492 			dm->brightness[aconnector->bl_idx] = real_brightness;
493 		}
494 		drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
495 	}
496 }
497 EXPORT_IF_KUNIT(amdgpu_dm_register_backlight_device);
498 
499 void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
500 {
501 	const struct drm_panel_backlight_quirk *panel_backlight_quirk;
502 	struct amdgpu_dm_backlight_caps *caps;
503 	struct drm_connector *conn_base;
504 	struct amdgpu_device *adev;
505 	struct drm_luminance_range_info *luminance_range;
506 	struct drm_device *drm;
507 
508 	if (aconnector->bl_idx == -1 ||
509 	    aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
510 		return;
511 
512 	conn_base = &aconnector->base;
513 	drm = conn_base->dev;
514 	adev = drm_to_adev(drm);
515 
516 	caps = &adev->dm.backlight_caps[aconnector->bl_idx];
517 	caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
518 	caps->aux_support = false;
519 
520 	panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
521 
522 	if (caps->ext_caps->bits.oled == 1
523 	    /*
524 	     * ||
525 	     * caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
526 	     * caps->ext_caps->bits.hdr_aux_backlight_control == 1
527 	     */)
528 		caps->aux_support = true;
529 
530 	if (amdgpu_backlight == 0)
531 		caps->aux_support = false;
532 	else if (amdgpu_backlight == 1)
533 		caps->aux_support = true;
534 	else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
535 		 panel_backlight_quirk->force_pwm)
536 		caps->aux_support = false;
537 	if (caps->aux_support) {
538 		if (aconnector->dc_link->dpcd_caps.panel_luminance_control)
539 			aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_VESA_AUX;
540 		else
541 			aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
542 	}
543 
544 	luminance_range = &conn_base->display_info.luminance_range;
545 
546 	if (luminance_range->max_luminance)
547 		caps->aux_max_input_signal = luminance_range->max_luminance;
548 	else
549 		caps->aux_max_input_signal = 512;
550 
551 	if (luminance_range->min_luminance)
552 		caps->aux_min_input_signal = luminance_range->min_luminance;
553 	else
554 		caps->aux_min_input_signal = 1;
555 
556 	if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
557 		if (panel_backlight_quirk->min_brightness) {
558 			caps->min_input_signal =
559 				panel_backlight_quirk->min_brightness - 1;
560 			drm_info(drm,
561 				 "Applying panel backlight quirk, min_brightness: %d\n",
562 				 caps->min_input_signal);
563 		}
564 		if (panel_backlight_quirk->brightness_mask) {
565 			drm_info(drm,
566 				 "Applying panel backlight quirk, brightness_mask: 0x%X\n",
567 				 panel_backlight_quirk->brightness_mask);
568 			caps->brightness_mask =
569 				panel_backlight_quirk->brightness_mask;
570 		}
571 	}
572 }
573 EXPORT_IF_KUNIT(amdgpu_dm_update_connector_ext_caps);
574 
575 void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm,
576 			    struct amdgpu_dm_connector *aconnector)
577 {
578 	struct dc_link *link = aconnector->dc_link;
579 	int bl_idx = dm->num_of_edps;
580 
581 	if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) ||
582 	    link->type == dc_connection_none)
583 		return;
584 
585 	if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) {
586 		drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n");
587 		return;
588 	}
589 
590 	aconnector->bl_idx = bl_idx;
591 
592 	amdgpu_dm_update_backlight_caps(dm, bl_idx);
593 	dm->backlight_link[bl_idx] = link;
594 	dm->num_of_edps++;
595 
596 	amdgpu_dm_update_connector_ext_caps(aconnector);
597 
598 	/* Offer ABM property when user didn't turn off by module parameter.
599 	 * OLED panels are included to support CACP (Content Adaptive
600 	 * Contrast and Power) feature via set_abm_level.
601 	 */
602 	if (amdgpu_dm_abm_level < 0)
603 		drm_object_attach_property(&aconnector->base.base,
604 					   dm->adev->mode_info.abm_level_property,
605 					   ABM_SYSFS_CONTROL);
606 }
607 EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device);
608 
609 /**
610  * DOC: panel power savings
611  *
612  * The display manager allows you to set your desired **panel power savings**
613  * level (between 0-4, with 0 representing off), e.g. using the following::
614  *
615  *   # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
616  *
617  * Modifying this value can have implications on color accuracy, so tread
618  * carefully.
619  */
620 
621 STATIC_IFN_KUNIT
622 ssize_t panel_power_savings_show(struct device *device,
623 				 struct device_attribute *attr,
624 				 char *buf)
625 {
626 	struct drm_connector *connector = dev_get_drvdata(device);
627 	struct drm_device *dev = connector->dev;
628 	u8 val;
629 
630 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
631 	val = to_dm_connector_state(connector->state)->abm_level ==
632 		ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
633 		to_dm_connector_state(connector->state)->abm_level;
634 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
635 
636 	return sysfs_emit(buf, "%u\n", val);
637 }
638 EXPORT_IF_KUNIT(panel_power_savings_show);
639 
640 STATIC_IFN_KUNIT
641 ssize_t panel_power_savings_store(struct device *device,
642 				 struct device_attribute *attr,
643 				 const char *buf, size_t count)
644 {
645 	struct drm_connector *connector = dev_get_drvdata(device);
646 	struct drm_device *dev = connector->dev;
647 	long val;
648 	int ret;
649 
650 	ret = kstrtol(buf, 0, &val);
651 
652 	if (ret)
653 		return ret;
654 
655 	if (val < 0 || val > 4)
656 		return -EINVAL;
657 
658 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
659 	if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
660 		ret = -EBUSY;
661 	else
662 		to_dm_connector_state(connector->state)->abm_level = val ?:
663 			ABM_LEVEL_IMMEDIATE_DISABLE;
664 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
665 
666 	if (ret)
667 		return ret;
668 
669 	drm_kms_helper_hotplug_event(dev);
670 
671 	return count;
672 }
673 EXPORT_IF_KUNIT(panel_power_savings_store);
674 
675 static DEVICE_ATTR_RW(panel_power_savings);
676 
677 static struct attribute *amdgpu_attrs[] = {
678 	&dev_attr_panel_power_savings.attr,
679 	NULL
680 };
681 
682 const struct attribute_group amdgpu_group = {
683 	.name = "amdgpu",
684 	.attrs = amdgpu_attrs
685 };
686 
687 bool
688 amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
689 {
690 	struct dc_link *link = amdgpu_dm_connector->dc_link;
691 
692 	if (amdgpu_dm_abm_level >= 0)
693 		return false;
694 
695 	if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
696 		return false;
697 
698 	if (link->panel_type != PANEL_TYPE_LCD && !link->panel_config.cacp.cacp_supported)
699 		return false;
700 
701 	return true;
702 }
703 EXPORT_IF_KUNIT(amdgpu_dm_should_create_sysfs);
704 
705 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
706 uint amdgpu_dm_get_dc_debug_mask(void)
707 {
708 	return amdgpu_dc_debug_mask;
709 }
710 EXPORT_IF_KUNIT(amdgpu_dm_get_dc_debug_mask);
711 
712 void amdgpu_dm_set_dc_debug_mask(uint val)
713 {
714 	amdgpu_dc_debug_mask = val;
715 }
716 EXPORT_IF_KUNIT(amdgpu_dm_set_dc_debug_mask);
717 
718 int amdgpu_dm_get_abm_level_param(void)
719 {
720 	return amdgpu_dm_abm_level;
721 }
722 EXPORT_IF_KUNIT(amdgpu_dm_get_abm_level_param);
723 
724 void amdgpu_dm_set_abm_level_param(int val)
725 {
726 	amdgpu_dm_abm_level = val;
727 }
728 EXPORT_IF_KUNIT(amdgpu_dm_set_abm_level_param);
729 
730 int amdgpu_dm_get_backlight_param(void)
731 {
732 	return amdgpu_backlight;
733 }
734 EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param);
735 
736 void amdgpu_dm_set_backlight_param(int val)
737 {
738 	amdgpu_backlight = val;
739 }
740 EXPORT_IF_KUNIT(amdgpu_dm_set_backlight_param);
741 #endif
742