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
amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager * dm,int bl_idx)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
get_brightness_range(const struct amdgpu_dm_backlight_caps * caps,unsigned int * min,unsigned int * max)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]. */
scale_input_to_fw(int max,u64 input)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] */
scale_fw_to_input(int min,int max,u64 input)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
convert_custom_brightness(const struct amdgpu_dm_backlight_caps * caps,unsigned int min,unsigned int max,uint32_t * user_brightness)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
convert_brightness_from_user(const struct amdgpu_dm_backlight_caps * caps,uint32_t brightness)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
convert_brightness_to_user(const struct amdgpu_dm_backlight_caps * caps,uint32_t brightness)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
dm_find_stream_with_link(struct amdgpu_display_manager * dm,struct dc_link * link)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
amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager * dm,struct backlight_device * bd)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
amdgpu_dm_backlight_set_level(struct amdgpu_display_manager * dm,int bl_idx,u32 user_brightness)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
amdgpu_dm_backlight_update_status(struct backlight_device * bd)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
amdgpu_dm_backlight_get_level(struct amdgpu_display_manager * dm,int bl_idx)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
amdgpu_dm_backlight_get_brightness(struct backlight_device * bd)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
amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps * caps,bool is_system_supplied,bool custom_curve_enabled,struct backlight_properties * props)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
amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector * aconnector)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
amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector * aconnector)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
amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager * dm,struct amdgpu_dm_connector * aconnector)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
panel_power_savings_show(struct device * device,struct device_attribute * attr,char * buf)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
panel_power_savings_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector * amdgpu_dm_connector)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)
amdgpu_dm_get_dc_debug_mask(void)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
amdgpu_dm_set_dc_debug_mask(uint val)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
amdgpu_dm_get_abm_level_param(void)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
amdgpu_dm_set_abm_level_param(int val)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
amdgpu_dm_get_backlight_param(void)730 int amdgpu_dm_get_backlight_param(void)
731 {
732 return amdgpu_backlight;
733 }
734 EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param);
735
amdgpu_dm_set_backlight_param(int val)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