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