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 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 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]. */ 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] */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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 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 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 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 796 int amdgpu_dm_get_backlight_param(void) 797 { 798 return amdgpu_backlight; 799 } 800 EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param); 801 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