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