summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-06-29 18:43:57 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-07-15 09:15:39 -0400
commitdb546ee97153c95ea719da015cf002f970175605 (patch)
treeb13959aac3ffddce004c10979db53d90a571f2ac
parent0198bf2a7c707bf1a73cda80df7200f7317520f5 (diff)
downloadlinux-stable-db546ee97153c95ea719da015cf002f970175605.tar.gz
linux-stable-db546ee97153c95ea719da015cf002f970175605.zip
drm/amd/display: Test panel power savings sysfs
[WHAT] Add KUnit coverage using a DRM-managed connector fixture. Tests cover show() mapping immediate-disable to 0 and reporting an active ABM level, and store() handling the disable mapping, the forbidden update, invalid text, and out-of-range input. show() writes through a page-backed buffer because sysfs_emit() requires one. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: George Zhang <george.zhang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c16
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h8
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c177
3 files changed, 195 insertions, 6 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index 859cbce2cdaf..373ef4d217f1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -611,9 +611,10 @@ EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device);
* carefully.
*/
-static ssize_t panel_power_savings_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
+STATIC_IFN_KUNIT
+ssize_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;
@@ -627,10 +628,12 @@ static ssize_t panel_power_savings_show(struct device *device,
return sysfs_emit(buf, "%u\n", val);
}
+EXPORT_IF_KUNIT(panel_power_savings_show);
-static ssize_t panel_power_savings_store(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
+STATIC_IFN_KUNIT
+ssize_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;
@@ -660,6 +663,7 @@ static ssize_t panel_power_savings_store(struct device *device,
return count;
}
+EXPORT_IF_KUNIT(panel_power_savings_store);
static DEVICE_ATTR_RW(panel_power_savings);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
index 7b417c79980f..07b75064847c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
@@ -30,6 +30,8 @@ struct backlight_device;
struct backlight_properties;
struct dc_link;
struct dc_stream_state;
+struct device;
+struct device_attribute;
struct drm_connector;
struct attribute_group;
@@ -56,6 +58,12 @@ struct dc_stream_state *dm_find_stream_with_link(struct amdgpu_display_manager *
int amdgpu_dm_backlight_update_status(struct backlight_device *bd);
u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, int bl_idx);
int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd);
+ssize_t panel_power_savings_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf);
+ssize_t panel_power_savings_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
unsigned int *min, unsigned int *max);
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
index 5a2c32d53487..b74bbbc7f56f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
@@ -8,6 +8,11 @@
#include <kunit/test.h>
#include <linux/backlight.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_mode_config.h>
+#include <drm/drm_property.h>
+
#include "dc.h"
#include "dc_dmub_srv.h"
#include "amdgpu.h"
@@ -458,6 +463,171 @@ static void dm_test_register_backlight_device_negative_index(struct kunit *test)
KUNIT_EXPECT_NULL(test, adev->dm.backlight_dev[0]);
}
+static struct drm_connector *setup_panel_power_savings_connector(struct kunit *test,
+ struct device **device_out,
+ struct dm_connector_state **state_out)
+{
+ struct dm_connector_state *state;
+ struct drm_connector *connector;
+ struct amdgpu_device *adev;
+ struct device *device;
+ int ret;
+
+ adev = dm_kunit_alloc_adev(test);
+ ret = drmm_mode_config_init(&adev->ddev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ connector->dev = &adev->ddev;
+ connector->state = &state->base;
+ dev_set_drvdata(device, connector);
+ *device_out = device;
+ *state_out = state;
+
+ return connector;
+}
+
+static void dm_test_free_sysfs_buf(void *data)
+{
+ free_page((unsigned long)data);
+}
+
+static char *dm_test_alloc_sysfs_buf(struct kunit *test)
+{
+ char *buf;
+
+ buf = (char *)get_zeroed_page(GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_sysfs_buf, buf), 0);
+
+ return buf;
+}
+
+/* Tests for panel_power_savings_show()/panel_power_savings_store() */
+
+/**
+ * dm_test_panel_power_savings_show_maps_disable_to_zero - Test show output
+ * @test: The KUnit test context
+ */
+static void dm_test_panel_power_savings_show_maps_disable_to_zero(struct kunit *test)
+{
+ struct dm_connector_state *state;
+ struct device *device;
+ char *buf;
+
+ setup_panel_power_savings_connector(test, &device, &state);
+ buf = dm_test_alloc_sysfs_buf(test);
+ state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
+
+ KUNIT_EXPECT_EQ(test, panel_power_savings_show(device, NULL, buf), 2);
+ KUNIT_EXPECT_STREQ(test, buf, "0\n");
+}
+
+/**
+ * dm_test_panel_power_savings_show_reports_level - Test show output for active level
+ * @test: The KUnit test context
+ *
+ * When abm_level is not the immediate-disable sentinel, show() reports the
+ * raw level value.
+ */
+static void dm_test_panel_power_savings_show_reports_level(struct kunit *test)
+{
+ struct dm_connector_state *state;
+ struct device *device;
+ char *buf;
+
+ setup_panel_power_savings_connector(test, &device, &state);
+ buf = dm_test_alloc_sysfs_buf(test);
+ state->abm_level = 3;
+
+ KUNIT_EXPECT_EQ(test, panel_power_savings_show(device, NULL, buf), 2);
+ KUNIT_EXPECT_STREQ(test, buf, "3\n");
+}
+
+/**
+ * dm_test_panel_power_savings_store_sets_disable - Test zero maps to disable
+ * @test: The KUnit test context
+ */
+static void dm_test_panel_power_savings_store_sets_disable(struct kunit *test)
+{
+ struct dm_connector_state *state;
+ struct device *device;
+ size_t count = strlen("0");
+
+ setup_panel_power_savings_connector(test, &device, &state);
+
+ KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "0", count),
+ (ssize_t)count);
+ KUNIT_EXPECT_EQ(test, state->abm_level, ABM_LEVEL_IMMEDIATE_DISABLE);
+}
+
+/**
+ * dm_test_panel_power_savings_store_forbidden - Test forbidden update
+ * @test: The KUnit test context
+ */
+static void dm_test_panel_power_savings_store_forbidden(struct kunit *test)
+{
+ struct dm_connector_state *state;
+ struct device *device;
+
+ setup_panel_power_savings_connector(test, &device, &state);
+ state->abm_sysfs_forbidden = true;
+
+ KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "1", 1), -EBUSY);
+}
+
+/**
+ * dm_test_panel_power_savings_store_rejects_invalid_text - Test parse failure
+ * @test: The KUnit test context
+ */
+static void dm_test_panel_power_savings_store_rejects_invalid_text(struct kunit *test)
+{
+ struct drm_connector *connector;
+ struct drm_device *drm;
+ struct device *device;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ drm = kunit_kzalloc(test, sizeof(*drm), GFP_KERNEL);
+ device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
+
+ connector->dev = drm;
+ dev_set_drvdata(device, connector);
+
+ KUNIT_EXPECT_LT(test, panel_power_savings_store(device, NULL, "bad", 3), 0);
+}
+
+/**
+ * dm_test_panel_power_savings_store_rejects_out_of_range - Test range failure
+ * @test: The KUnit test context
+ */
+static void dm_test_panel_power_savings_store_rejects_out_of_range(struct kunit *test)
+{
+ struct drm_connector *connector;
+ struct drm_device *drm;
+ struct device *device;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ drm = kunit_kzalloc(test, sizeof(*drm), GFP_KERNEL);
+ device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
+
+ connector->dev = drm;
+ dev_set_drvdata(device, connector);
+
+ KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "5", 1), -EINVAL);
+}
+
/* Tests for amdgpu_dm_backlight_get_device_index() */
/**
@@ -1636,6 +1806,13 @@ static struct kunit_case dm_backlight_test_cases[] = {
KUNIT_CASE(dm_test_backlight_get_brightness_uses_device_index),
/* amdgpu_dm_register_backlight_device */
KUNIT_CASE(dm_test_register_backlight_device_negative_index),
+ /* panel_power_savings_show / store */
+ KUNIT_CASE(dm_test_panel_power_savings_show_maps_disable_to_zero),
+ KUNIT_CASE(dm_test_panel_power_savings_show_reports_level),
+ KUNIT_CASE(dm_test_panel_power_savings_store_sets_disable),
+ KUNIT_CASE(dm_test_panel_power_savings_store_forbidden),
+ KUNIT_CASE(dm_test_panel_power_savings_store_rejects_invalid_text),
+ KUNIT_CASE(dm_test_panel_power_savings_store_rejects_out_of_range),
/* amdgpu_dm_backlight_get_device_index */
KUNIT_CASE(dm_test_backlight_device_index_matches_second),
KUNIT_CASE(dm_test_backlight_device_index_missing_fallback),