Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817// SPDX-License-Identifier: MIT/* * Copyright 2026 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD */
#include "dc.h"#include "dc/dc_dmub_srv.h"#include "dc/dc_state.h"#include "dc/dc_stat.h"
#include "amdgpu.h"#include "amdgpu_display.h"#include "amdgpu_dm.h"#include "amdgpu_dm_backlight.h"#include "amdgpu_dm_psr.h"#include "amdgpu_dm_replay.h"#include "amdgpu_atombios.h"
#include "modules/inc/mod_power.h"
#include <linux/backlight.h>#include <linux/minmax.h>#include <linux/power_supply.h>#include <drm/drm_edid.h>#include <drm/drm_panel_quirks.h>
#include <acpi/video.h>
#include "amdgpu_dm_trace.h"#include "amd_shared.h"#include "dm_helpers.h"
#if defined(CONFIG_ACPI) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)STATIC_IFN_KUNITvoid amdgpu_dm_validate_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx){ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx]; int spread = caps->max_input_signal - caps->min_input_signal;
if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || caps->min_input_signal < 0 || spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || spread < AMDGPU_DM_MIN_SPREAD) { drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n", caps->min_input_signal, caps->max_input_signal); caps->caps_valid = false; }}EXPORT_IF_KUNIT(amdgpu_dm_validate_backlight_caps);#endif
void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx){ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
if (caps->caps_valid) return;
#if defined(CONFIG_ACPI) amdgpu_acpi_get_backlight_caps(caps);
/* validate the firmware value is sane */ if (caps->caps_valid) amdgpu_dm_validate_backlight_caps(dm, bl_idx);#else if (caps->aux_support) return;#endif if (!caps->caps_valid) { caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; caps->ac_level = caps->dc_level = 50; caps->caps_valid = true; }}EXPORT_IF_KUNIT(amdgpu_dm_update_backlight_caps);
STATIC_IFN_KUNITint get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, unsigned int *min, unsigned int *max){ if (!caps) return 0;
if (caps->aux_support) { /* Firmware limits are in nits, DC API wants millinits. */ *max = 1000 * caps->aux_max_input_signal; *min = 1000 * caps->aux_min_input_signal; } else { /* Firmware limits are 8-bit, PWM control is 16-bit. */ *max = 0x101 * caps->max_input_signal; *min = 0x101 * caps->min_input_signal; } return 1;}EXPORT_IF_KUNIT(get_brightness_range);
/* Rescale userspace [0..max] to the firmware curve's [0..255]. */static inline u32 scale_input_to_fw(int max, u64 input){ return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max);}
/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */static inline u32 scale_fw_to_input(int min, int max, u64 input){ return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);}
static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps, u32 signal){ u32 lower_signal, upper_signal; u32 lower_lum, upper_lum; int i;
if (!caps->data_points) return DIV_ROUND_CLOSEST(signal * 100, AMDGPU_MAX_BL_LEVEL);
if (signal < caps->luminance_data[0].input_signal) { lower_signal = 0; lower_lum = 0; upper_signal = caps->luminance_data[0].input_signal; upper_lum = caps->luminance_data[0].luminance; } else if (signal >= caps->luminance_data[caps->data_points - 1].input_signal) { lower_signal = caps->luminance_data[caps->data_points - 1].input_signal; lower_lum = caps->luminance_data[caps->data_points - 1].luminance; upper_signal = caps->max_input_signal; upper_lum = 100; } else { for (i = 1; i < caps->data_points && signal > caps->luminance_data[i].input_signal; i++) ;
lower_signal = caps->luminance_data[i - 1].input_signal; upper_signal = caps->luminance_data[i].input_signal; lower_lum = caps->luminance_data[i - 1].luminance; upper_lum = caps->luminance_data[i].luminance; }
if (upper_signal == lower_signal) return upper_lum;
return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * (signal - lower_signal), upper_signal - lower_signal);}
static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps, u32 user_brightness){ unsigned int min, max; u32 signal, luminance;
if (!get_brightness_range(caps, &min, &max)) return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, MAX_BACKLIGHT_LEVEL);
user_brightness = clamp(user_brightness, min, max); if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, max);
signal = scale_input_to_fw(max, user_brightness); luminance = backlight_signal_to_luminance(caps, signal);
return min_t(u32, luminance * 1000, 100000);}
STATIC_IFN_KUNITvoid convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, unsigned int min, unsigned int max, uint32_t *user_brightness){ u32 brightness = scale_input_to_fw(max, *user_brightness); u8 lower_signal, upper_signal, upper_lum, lower_lum, lum; int left, right;
if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) return;
if (!caps->data_points) return;
/* * Handle the case where brightness is below the first data point * Interpolate between (0,0) and (first_signal, first_lum) */ if (brightness < caps->luminance_data[0].input_signal) { lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness, caps->luminance_data[0].input_signal); goto scale; }
left = 0; right = caps->data_points - 1; while (left <= right) { int mid = left + (right - left) / 2; u8 signal = caps->luminance_data[mid].input_signal;
/* Exact match found */ if (signal == brightness) { lum = caps->luminance_data[mid].luminance; goto scale; }
if (signal < brightness) left = mid + 1; else right = mid - 1; }
/* verify bound */ if (left >= caps->data_points) left = caps->data_points - 1;
/* At this point, left > right */ lower_signal = caps->luminance_data[right].input_signal; upper_signal = caps->luminance_data[left].input_signal; lower_lum = caps->luminance_data[right].luminance; upper_lum = caps->luminance_data[left].luminance;
/* interpolate */ if (right == left || !lower_lum) lum = upper_lum; else lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * (brightness - lower_signal), upper_signal - lower_signal);scale: *user_brightness = scale_fw_to_input(min, max, DIV_ROUND_CLOSEST(lum * brightness, 101));}
EXPORT_IF_KUNIT(convert_custom_brightness);
STATIC_IFN_KUNITu32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps, uint32_t brightness){ unsigned int min, max;
if (!get_brightness_range(caps, &min, &max)) return brightness;
convert_custom_brightness(caps, min, max, &brightness);
/* Rescale 0..max to min..max */ return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);}
EXPORT_IF_KUNIT(convert_brightness_from_user);
STATIC_IFN_KUNITu32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps, uint32_t brightness){ unsigned int min, max;
if (!get_brightness_range(caps, &min, &max)) return brightness;
if (brightness < min) return 0; /* Rescale min..max to 0..max */ return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min), max - min);}EXPORT_IF_KUNIT(convert_brightness_to_user);
STATIC_IFN_KUNITstruct dc_stream_state *dm_find_stream_with_link( struct amdgpu_display_manager *dm, struct dc_link *link){ struct dc_state *cur_dc_state = dm->dc->current_state; struct dc_stream_state *stream = NULL; int i;
for (i = 0; i < cur_dc_state->stream_count; i++) { stream = cur_dc_state->streams[i]; if (stream->link == link) return stream; }
return NULL;}EXPORT_IF_KUNIT(dm_find_stream_with_link);
STATIC_IFN_KUNITint amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm, struct backlight_device *bd){ int i;
for (i = 0; i < dm->num_of_edps; i++) { if (bd == dm->backlight_dev[i]) return i; }
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_device_index);
void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, int bl_idx, u32 user_brightness){ struct amdgpu_dm_backlight_caps *caps; struct dc_link *link; u32 brightness = 0; u32 millipercent = 0; bool rc = false, reallow_idle = false; struct drm_connector *connector; struct dc_stream_state *stream;
list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
if (aconnector->bl_idx != bl_idx) continue;
/* if connector is off, save the brightness for next time it's on */ if (!aconnector->base.encoder) { dm->brightness[bl_idx] = user_brightness; dm->actual_brightness[bl_idx] = 0; return; } }
amdgpu_dm_update_backlight_caps(dm, bl_idx); caps = &dm->backlight_caps[bl_idx];
dm->brightness[bl_idx] = user_brightness; /* update scratch register */ if (bl_idx == 0) amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
link = (struct dc_link *)dm->backlight_link[bl_idx]; stream = dm_find_stream_with_link(dm, link); if (!stream) return;
if (caps->aux_support) { brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); /* Apply brightness quirk */ if (caps->brightness_mask) brightness |= caps->brightness_mask; } else { millipercent = convert_brightness_to_millipercent(caps, user_brightness); }
if (trace_amdgpu_dm_brightness_enabled()) { trace_amdgpu_dm_brightness(__builtin_return_address(0), user_brightness, brightness, caps->aux_support, power_supply_is_system_supplied() > 0); }
mutex_lock(&dm->dc_lock); if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { dc_allow_idle_optimizations(dm->dc, false); reallow_idle = true; }
if (caps->aux_support) { rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness, AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true); } else { /* power module uses millipercent */ rc = mod_power_set_backlight_percent(dm->power_module, stream, millipercent, 0, false); }
/* * Some kms clients create a ramped backlight transition effect * by rapidly changing the backlight. Yet we must wait on dmcub * fw to exit psr/replay before programming backlight. To * prevent lag, keep disable psr/replay and let the next atomic * flip clear the event. * * ToDo: use ISM to handle rapidly backlight change * * Rapidly backlight change is similar to rapidly cursor events, * which is now handled by ISM. ISM can delay the event until system * is really idle, so we may use ISM to handle backlight change as well. */ amdgpu_dm_psr_set_event(dm, stream, true, psr_event_hw_programming, true); amdgpu_dm_replay_set_event(dm, stream, true, replay_event_hw_programming, true);
if (dm->dc->caps.ips_support && reallow_idle) dc_allow_idle_optimizations(dm->dc, true);
mutex_unlock(&dm->dc_lock);
if (rc) dm->actual_brightness[bl_idx] = user_brightness;}EXPORT_IF_KUNIT(amdgpu_dm_backlight_set_level);
STATIC_IFN_KUNITint amdgpu_dm_backlight_update_status(struct backlight_device *bd){ struct amdgpu_display_manager *dm = bl_get_data(bd); int i = amdgpu_dm_backlight_get_device_index(dm, bd);
amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_backlight_update_status);
STATIC_IFN_KUNITu32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, int bl_idx){ int ret; struct amdgpu_dm_backlight_caps caps; struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
amdgpu_dm_update_backlight_caps(dm, bl_idx); caps = dm->backlight_caps[bl_idx];
if (caps.aux_support) { u32 avg, peak;
if (!dc_link_get_backlight_level_nits(link, &avg, &peak)) return dm->brightness[bl_idx]; return convert_brightness_to_user(&caps, avg); }
ret = dc_link_get_backlight_level(link);
if (ret == DC_ERROR_UNEXPECTED) return dm->brightness[bl_idx];
return convert_brightness_to_user(&caps, ret);}EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_level);
STATIC_IFN_KUNITint amdgpu_dm_backlight_get_brightness(struct backlight_device *bd){ struct amdgpu_display_manager *dm = bl_get_data(bd); int i = amdgpu_dm_backlight_get_device_index(dm, bd);
return amdgpu_dm_backlight_get_level(dm, i);}EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_brightness);
static const struct backlight_ops amdgpu_dm_backlight_ops = { .options = BL_CORE_SUSPENDRESUME, .get_brightness = amdgpu_dm_backlight_get_brightness, .update_status = amdgpu_dm_backlight_update_status,};
STATIC_IFN_KUNITvoid amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps, bool is_system_supplied, bool custom_curve_enabled, struct backlight_properties *props){ unsigned int min, max;
if (get_brightness_range(caps, &min, &max)) { if (is_system_supplied) props->brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100); else props->brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100); props->max_brightness = max; } else { props->brightness = MAX_BACKLIGHT_LEVEL; props->max_brightness = MAX_BACKLIGHT_LEVEL; }
if (caps && caps->data_points && custom_curve_enabled) props->scale = BACKLIGHT_SCALE_NON_LINEAR; else props->scale = BACKLIGHT_SCALE_LINEAR; props->type = BACKLIGHT_RAW;}EXPORT_IF_KUNIT(amdgpu_dm_backlight_fill_props);
voidamdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector){ struct drm_device *drm = aconnector->base.dev; struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm; struct backlight_properties props = { 0 }; struct amdgpu_dm_backlight_caps *caps; char bl_name[16]; int real_brightness; int init_brightness;
if (aconnector->bl_idx == -1) return;
if (!acpi_video_backlight_use_native()) { drm_info(drm, "Skipping amdgpu DM backlight registration\n"); /* Try registering an ACPI video backlight device instead. */ acpi_video_register_backlight(); return; }
caps = &dm->backlight_caps[aconnector->bl_idx]; amdgpu_dm_backlight_fill_props(caps, power_supply_is_system_supplied() > 0, !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE), &props); drm_dbg(drm, "Backlight caps: max_brightness: %d, ac %d, dc %d\n", props.max_brightness, caps->ac_level, caps->dc_level);
init_brightness = props.brightness;
if (props.scale == BACKLIGHT_SCALE_NON_LINEAR) drm_info(drm, "Using custom brightness curve\n");
snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", drm->primary->index + aconnector->bl_idx);
dm->backlight_dev[aconnector->bl_idx] = backlight_device_register(bl_name, aconnector->base.kdev, dm, &amdgpu_dm_backlight_ops, &props); dm->brightness[aconnector->bl_idx] = props.brightness;
if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) { drm_err(drm, "DM: Backlight registration failed!\n"); dm->backlight_dev[aconnector->bl_idx] = NULL; } else { /* * dm->brightness[x] can be inconsistent just after startup until * ops.get_brightness is called. */ real_brightness = amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
if (real_brightness != init_brightness) { dm->actual_brightness[aconnector->bl_idx] = real_brightness; dm->brightness[aconnector->bl_idx] = real_brightness; } drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name); }}EXPORT_IF_KUNIT(amdgpu_dm_register_backlight_device);
void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector){ const struct drm_panel_backlight_quirk *panel_backlight_quirk; struct amdgpu_dm_backlight_caps *caps; struct drm_connector *conn_base; struct amdgpu_device *adev; struct drm_luminance_range_info *luminance_range; struct drm_device *drm;
if (aconnector->bl_idx == -1 || aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP) return;
conn_base = &aconnector->base; drm = conn_base->dev; adev = drm_to_adev(drm);
caps = &adev->dm.backlight_caps[aconnector->bl_idx]; caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; caps->aux_support = false;
panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
if (caps->ext_caps->bits.oled == 1 /* * || * caps->ext_caps->bits.sdr_aux_backlight_control == 1 || * caps->ext_caps->bits.hdr_aux_backlight_control == 1 */) caps->aux_support = true;
if (amdgpu_backlight == 0) caps->aux_support = false; else if (amdgpu_backlight == 1) caps->aux_support = true; else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && panel_backlight_quirk->force_pwm) caps->aux_support = false; if (caps->aux_support) { if (aconnector->dc_link->dpcd_caps.panel_luminance_control) aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_VESA_AUX; else aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; }
luminance_range = &conn_base->display_info.luminance_range;
if (luminance_range->max_luminance) caps->aux_max_input_signal = luminance_range->max_luminance; else caps->aux_max_input_signal = 512;
if (luminance_range->min_luminance) caps->aux_min_input_signal = luminance_range->min_luminance; else caps->aux_min_input_signal = 1;
if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { if (panel_backlight_quirk->min_brightness) { caps->min_input_signal = panel_backlight_quirk->min_brightness - 1; drm_info(drm, "Applying panel backlight quirk, min_brightness: %d\n", caps->min_input_signal); } if (panel_backlight_quirk->brightness_mask) { drm_info(drm, "Applying panel backlight quirk, brightness_mask: 0x%X\n", panel_backlight_quirk->brightness_mask); caps->brightness_mask = panel_backlight_quirk->brightness_mask; } }}EXPORT_IF_KUNIT(amdgpu_dm_update_connector_ext_caps);
void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector){ struct dc_link *link = aconnector->dc_link; int bl_idx = dm->num_of_edps;
if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) || link->type == dc_connection_none) return;
if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) { drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n"); return; }
aconnector->bl_idx = bl_idx;
amdgpu_dm_update_backlight_caps(dm, bl_idx); dm->backlight_link[bl_idx] = link; dm->num_of_edps++;
amdgpu_dm_update_connector_ext_caps(aconnector);
/* Offer ABM property when user didn't turn off by module parameter. * OLED panels are included to support CACP (Content Adaptive * Contrast and Power) feature via set_abm_level. */ if (amdgpu_dm_abm_level < 0) drm_object_attach_property(&aconnector->base.base, dm->adev->mode_info.abm_level_property, ABM_SYSFS_CONTROL);}EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device);
/** * DOC: panel power savings * * The display manager allows you to set your desired **panel power savings** * level (between 0-4, with 0 representing off), e.g. using the following:: * * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings * * Modifying this value can have implications on color accuracy, so tread * carefully. */
STATIC_IFN_KUNITssize_t panel_power_savings_show(struct device *device, struct device_attribute *attr, char *buf){ struct drm_connector *connector = dev_get_drvdata(device); struct drm_device *dev = connector->dev; u8 val;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); val = to_dm_connector_state(connector->state)->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE ? 0 : to_dm_connector_state(connector->state)->abm_level; drm_modeset_unlock(&dev->mode_config.connection_mutex);
return sysfs_emit(buf, "%u\n", val);}EXPORT_IF_KUNIT(panel_power_savings_show);
STATIC_IFN_KUNITssize_t panel_power_savings_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count){ struct drm_connector *connector = dev_get_drvdata(device); struct drm_device *dev = connector->dev; long val; int ret;
ret = kstrtol(buf, 0, &val);
if (ret) return ret;
if (val < 0 || val > 4) return -EINVAL;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden) ret = -EBUSY; else to_dm_connector_state(connector->state)->abm_level = val ?: ABM_LEVEL_IMMEDIATE_DISABLE; drm_modeset_unlock(&dev->mode_config.connection_mutex);
if (ret) return ret;
drm_kms_helper_hotplug_event(dev);
return count;}EXPORT_IF_KUNIT(panel_power_savings_store);
static DEVICE_ATTR_RW(panel_power_savings);
static struct attribute *amdgpu_attrs[] = { &dev_attr_panel_power_savings.attr, NULL};
const struct attribute_group amdgpu_group = { .name = "amdgpu", .attrs = amdgpu_attrs};
boolamdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector){ struct dc_link *link = amdgpu_dm_connector->dc_link;
if (amdgpu_dm_abm_level >= 0) return false;
if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) return false;
if (link->panel_type != PANEL_TYPE_LCD && !link->panel_config.cacp.cacp_supported) return false;
return true;}EXPORT_IF_KUNIT(amdgpu_dm_should_create_sysfs);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)uint amdgpu_dm_get_dc_debug_mask(void){ return amdgpu_dc_debug_mask;}EXPORT_IF_KUNIT(amdgpu_dm_get_dc_debug_mask);
void amdgpu_dm_set_dc_debug_mask(uint val){ amdgpu_dc_debug_mask = val;}EXPORT_IF_KUNIT(amdgpu_dm_set_dc_debug_mask);
int amdgpu_dm_get_abm_level_param(void){ return amdgpu_dm_abm_level;}EXPORT_IF_KUNIT(amdgpu_dm_get_abm_level_param);
void amdgpu_dm_set_abm_level_param(int val){ amdgpu_dm_abm_level = val;}EXPORT_IF_KUNIT(amdgpu_dm_set_abm_level_param);
int amdgpu_dm_get_backlight_param(void){ return amdgpu_backlight;}EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param);
void amdgpu_dm_set_backlight_param(int val){ amdgpu_backlight = val;}EXPORT_IF_KUNIT(amdgpu_dm_set_backlight_param);#endif