On 2025-10-23 10:21, Mario Limonciello wrote:
> On 10/23/25 9:09 AM, Harry Wentland wrote:
>>
>>
>> On 2025-07-18 15:20, Mario Limonciello wrote:
>>> From: Mario Limonciello <[email protected]>
>>>
>>> commit 0887054d14ae2 ("drm/amd: Drop abm_level property") dropped the
>>> abm level property in favor of sysfs control. Since then there have
>>> been discussions that compositors showed an interest in modifying
>>> a vendor specific property instead.
>>>
>>> So re-introduce the abm level property, but with different semantics.
>>> Rather than being an integer it's now an enum. One of the enum options
>>> is 'sysfs', and that is because there is still a sysfs file for use by
>>> userspace when the compositor doesn't support this property.
>>>
>>> If usespace has not modified this property, the default value will
>>> be for sysfs to control it. Once userspace has set the property stop
>>> allowing sysfs control.
>>>
>>> The property is only attached to non-OLED eDP panels.
>>>
>>> Cc: Xaver Hugl <[email protected]>
>>> Signed-off-by: Mario Limonciello <[email protected]>
>>> ---
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_display.c   | 60 ++++++++++++++++++-
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_display.h   |  7 +++
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h      |  2 +
>>>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 39 +++++++++++-
>>>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  1 +
>>>   5 files changed, 106 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c 
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
>>> index 35c778426a7c7..f061f63e31993 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
>>> @@ -1363,6 +1363,64 @@ static const struct drm_prop_enum_list 
>>> amdgpu_dither_enum_list[] = {
>>>       { AMDGPU_FMT_DITHER_ENABLE, "on" },
>>>   };
>>>   +/**
>>> + * DOC: property for adaptive backlight modulation
>>> + *
>>> + * The 'adaptive backlight modulation' property is used for the compositor 
>>> to
>>> + * directly control the adaptive backlight modulation power savings feature
>>> + * that is part of DCN hardware.
>>> + *
>>> + * The property will be attached specifically to eDP panels that support 
>>> it.
>>> + *
>>> + * The property is by default set to 'sysfs' to allow the sysfs file 
>>> 'panel_power_savings'
>>> + * to be able to control it.
>>> + * If set to 'off' the compositor will ensure it stays off.
>>> + * The other values 'min', 'bias min', 'bias max', and 'max' will control 
>>> the
>>> + * intensity of the power savings.
>>> + *
>>> + * Modifying this value can have implications on color accuracy, so tread
>>> + * carefully.
>>> + */
>>> +static int amdgpu_display_setup_abm_prop(struct amdgpu_device *adev)
>>> +{
>>> +    const struct drm_prop_enum_list props[] = {
>>> +        { ABM_SYSFS_CONTROL, "sysfs" },
>>> +        { ABM_LEVEL_OFF, "off" },
>>> +        { ABM_LEVEL_MIN, "min" },
>>> +        { ABM_LEVEL_BIAS_MIN, "bias min" },
>>> +        { ABM_LEVEL_BIAS_MAX, "bias max" },
>>> +        { ABM_LEVEL_MAX, "max" },
>>
>> My only concern is that with these values we're locking ourselves
>> specifically to ABM. But if userspace implements support one might
>> want to use that support for other, similar panel power saving
>> features, like some OLED power saving features.
>>
>> I was thinking a range property might work better and could see
>> re-use in the future, but unfortunately it uses uints to specify
>> the range, so we couldn't designate -1 for "sysfs".
> 
> Yeah that was exactly why I landed on an enum.
> 
>>
>> Thoughts? Should we care? Can we avoid userspace needing code for
>> a set of different panel power saving features, like
>> - ABM
>> - OLED power saving
>> - <some Intel panel power saving feature>
>> - <another NVidia panel power saving feature>
>> - <other vendors panel power saving features>
>>
>> If we made it more generic we could even move this to a common DRM
>> property once other drivers have use for it.
> 
> It sounds like a flip flop between the older version (generic "panel power 
> savings" property) and this one.
> 
> During the hackfest this year Xaver had made the point we're always going to 
> end up with subtle nuance in different implementations of power saving 
> technologies that the compositor may need to care about, which I tend to 
> agree with.
>>

Fair enough.

Patch is
Reviewed-by: Harry Wentland <[email protected]>

Harry

>> Harry
>>
>>> +    };
>>> +    struct drm_property *prop;
>>> +    int i;
>>> +
>>> +    if (!adev->dc_enabled)
>>> +        return 0;
>>> +
>>> +    prop = drm_property_create(adev_to_drm(adev), DRM_MODE_PROP_ENUM,
>>> +                "adaptive backlight modulation",
>>> +                6);
>>> +    if (!prop)
>>> +        return -ENOMEM;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(props); i++) {
>>> +        int ret;
>>> +
>>> +        ret = drm_property_add_enum(prop, props[i].type,
>>> +                        props[i].name);
>>> +
>>> +        if (ret) {
>>> +            drm_property_destroy(adev_to_drm(adev), prop);
>>> +
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    adev->mode_info.abm_level_property = prop;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
>>>   {
>>>       int sz;
>>> @@ -1409,7 +1467,7 @@ int amdgpu_display_modeset_create_props(struct 
>>> amdgpu_device *adev)
>>>                        "dither",
>>>                        amdgpu_dither_enum_list, sz);
>>>   -    return 0;
>>> +    return amdgpu_display_setup_abm_prop(adev);
>>>   }
>>>     void amdgpu_display_update_priority(struct amdgpu_device *adev)
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h 
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
>>> index dfa0d642ac161..2b1536a167527 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
>>> @@ -54,4 +54,11 @@ int amdgpu_display_resume_helper(struct amdgpu_device 
>>> *adev);
>>>   int amdgpu_display_get_scanout_buffer(struct drm_plane *plane,
>>>                         struct drm_scanout_buffer *sb);
>>>   +#define ABM_SYSFS_CONTROL    -1
>>> +#define ABM_LEVEL_OFF        0
>>> +#define ABM_LEVEL_MIN        1
>>> +#define ABM_LEVEL_BIAS_MIN    2
>>> +#define ABM_LEVEL_BIAS_MAX    3
>>> +#define ABM_LEVEL_MAX        4
>>> +
>>>   #endif
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h 
>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
>>> index 6da4f946cac00..169bc667572e2 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
>>> @@ -326,6 +326,8 @@ struct amdgpu_mode_info {
>>>       struct drm_property *audio_property;
>>>       /* FMT dithering */
>>>       struct drm_property *dither_property;
>>> +    /* Adaptive Backlight Modulation (power feature) */
>>> +    struct drm_property *abm_level_property;
>>>       /* hardcoded DFP edid from BIOS */
>>>       const struct drm_edid *bios_hardcoded_edid;
>>>   diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
>>> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>> index 928f61f972a1f..4025575d2f536 100644
>>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>> @@ -5042,6 +5042,7 @@ static int initialize_plane(struct 
>>> amdgpu_display_manager *dm,
>>>   static void setup_backlight_device(struct amdgpu_display_manager *dm,
>>>                      struct amdgpu_dm_connector *aconnector)
>>>   {
>>> +    struct amdgpu_dm_backlight_caps *caps;
>>>       struct dc_link *link = aconnector->dc_link;
>>>       int bl_idx = dm->num_of_edps;
>>>   @@ -5061,6 +5062,13 @@ static void setup_backlight_device(struct 
>>> amdgpu_display_manager *dm,
>>>       dm->num_of_edps++;
>>>         update_connector_ext_caps(aconnector);
>>> +    caps = &dm->backlight_caps[aconnector->bl_idx];
>>> +
>>> +    /* Only offer ABM property when non-OLED and user didn't turn off by 
>>> module parameter */
>>> +    if (!caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
>>> +        drm_object_attach_property(&aconnector->base.base,
>>> +                       dm->adev->mode_info.abm_level_property,
>>> +                       ABM_SYSFS_CONTROL);
>>>   }
>>>     static void amdgpu_set_panel_orientation(struct drm_connector 
>>> *connector);
>>> @@ -7122,6 +7130,20 @@ int amdgpu_dm_connector_atomic_set_property(struct 
>>> drm_connector *connector,
>>>       } else if (property == adev->mode_info.underscan_property) {
>>>           dm_new_state->underscan_enable = val;
>>>           ret = 0;
>>> +    } else if (property == adev->mode_info.abm_level_property) {
>>> +        switch (val) {
>>> +        case ABM_SYSFS_CONTROL:
>>> +            dm_new_state->abm_sysfs_forbidden = false;
>>> +            break;
>>> +        case ABM_LEVEL_OFF:
>>> +            dm_new_state->abm_sysfs_forbidden = true;
>>> +            dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
>>> +            break;
>>> +        default:
>>> +            dm_new_state->abm_sysfs_forbidden = true;
>>> +            dm_new_state->abm_level = val;
>>> +        };
>>> +        ret = 0;
>>>       }
>>>         return ret;
>>> @@ -7164,6 +7186,13 @@ int amdgpu_dm_connector_atomic_get_property(struct 
>>> drm_connector *connector,
>>>       } else if (property == adev->mode_info.underscan_property) {
>>>           *val = dm_state->underscan_enable;
>>>           ret = 0;
>>> +    } else if (property == adev->mode_info.abm_level_property) {
>>> +        if (!dm_state->abm_sysfs_forbidden)
>>> +            *val = ABM_SYSFS_CONTROL;
>>> +        else
>>> +            *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
>>> +                dm_state->abm_level : 0;
>>> +        ret = 0;
>>>       }
>>>         return ret;
>>> @@ -7216,10 +7245,16 @@ static ssize_t panel_power_savings_store(struct 
>>> device *device,
>>>           return -EINVAL;
>>>         drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>>> -    to_dm_connector_state(connector->state)->abm_level = val ?:
>>> -        ABM_LEVEL_IMMEDIATE_DISABLE;
>>> +    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;
>>> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
>>> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
>>> index 0b964bfdd4a55..5a38373e054a8 100644
>>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
>>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
>>> @@ -969,6 +969,7 @@ struct dm_connector_state {
>>>       bool underscan_enable;
>>>       bool freesync_capable;
>>>       bool update_hdcp;
>>> +    bool abm_sysfs_forbidden;
>>>       uint8_t abm_level;
>>>       int vcpi_slots;
>>>       uint64_t pbn;
>>
> 

Reply via email to