mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
drm/amd/display: Add crtc matching and panel type tests for connector
Add KUnit coverage for amdgpu_dm_find_first_crtc_matching_connector(): match, no match, empty state, skipping NULL connector slots, and returning the first match when several target the same crtc. Also add coverage for amdgpu_dm_set_panel_type(): VSDB OLED/miniLED, DPCD OLED/miniLED, Samsung miniLED above and below threshold, and the default LCD fallback. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: George Zhang <george.zhang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
eff243192f
commit
1803ad56ab
|
|
@ -394,6 +394,7 @@ amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
|
|||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_find_first_crtc_matching_connector);
|
||||
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ int amdgpu_dm_encoder_init(struct drm_device *dev,
|
|||
enum drm_mode_subconnector get_subconnector_type(struct dc_link *link);
|
||||
void update_subconnector_property(struct amdgpu_dm_connector *aconnector);
|
||||
void amdgpu_dm_fbc_init(struct drm_connector *connector);
|
||||
void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector);
|
||||
enum display_content_type
|
||||
get_output_content_type(const struct drm_connector_state *connector_state);
|
||||
bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@
|
|||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_mode_object.h>
|
||||
#include <drm/drm_property.h>
|
||||
#include <linux/hdmi.h>
|
||||
|
||||
#include "dc.h"
|
||||
|
|
@ -2896,6 +2900,305 @@ static void dm_test_detect_mst_branch_without_aux(struct kunit *test)
|
|||
amdgpu_dm_detect_mst_link_for_all_connectors(drm), 0);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_find_first_crtc_matching_connector() */
|
||||
|
||||
/*
|
||||
* Build a minimal drm_atomic_commit holding @count connector slots. The
|
||||
* function under test only reads num_connector, connectors[i].ptr and
|
||||
* connectors[i].new_state, so a hand-rolled state is sufficient.
|
||||
*/
|
||||
static struct drm_atomic_commit *
|
||||
dm_test_alloc_atomic_state(struct kunit *test, int count)
|
||||
{
|
||||
struct drm_atomic_commit *state;
|
||||
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, state);
|
||||
|
||||
state->num_connector = count;
|
||||
if (count) {
|
||||
state->connectors = kunit_kcalloc(test, count,
|
||||
sizeof(*state->connectors),
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, state->connectors);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_first_crtc_match - Test find_first_crtc returns matching connector
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_first_crtc_match(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_commit *state = dm_test_alloc_atomic_state(test, 1);
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *con_state;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
con_state = kunit_kzalloc(test, sizeof(*con_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, con_state);
|
||||
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, crtc);
|
||||
|
||||
con_state->crtc = crtc;
|
||||
state->connectors[0].ptr = connector;
|
||||
state->connectors[0].new_state = con_state;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test,
|
||||
amdgpu_dm_find_first_crtc_matching_connector(state, crtc),
|
||||
connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_first_crtc_no_match - Test find_first_crtc returns NULL when no crtc matches
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_first_crtc_no_match(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_commit *state = dm_test_alloc_atomic_state(test, 1);
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *con_state;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc *other_crtc;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
con_state = kunit_kzalloc(test, sizeof(*con_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, con_state);
|
||||
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, crtc);
|
||||
other_crtc = kunit_kzalloc(test, sizeof(*other_crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, other_crtc);
|
||||
|
||||
con_state->crtc = other_crtc;
|
||||
state->connectors[0].ptr = connector;
|
||||
state->connectors[0].new_state = con_state;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
amdgpu_dm_find_first_crtc_matching_connector(state, crtc));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_first_crtc_empty_state - Test find_first_crtc returns NULL with no connectors
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_first_crtc_empty_state(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_commit *state = dm_test_alloc_atomic_state(test, 0);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, crtc);
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
amdgpu_dm_find_first_crtc_matching_connector(state, crtc));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_first_crtc_skips_null_ptr - Test find_first_crtc skips empty connector slots
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_first_crtc_skips_null_ptr(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_commit *state = dm_test_alloc_atomic_state(test, 2);
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *con_state;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
con_state = kunit_kzalloc(test, sizeof(*con_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, con_state);
|
||||
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, crtc);
|
||||
|
||||
/* Slot 0 has no connector (ptr == NULL) and must be skipped. */
|
||||
con_state->crtc = crtc;
|
||||
state->connectors[1].ptr = connector;
|
||||
state->connectors[1].new_state = con_state;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test,
|
||||
amdgpu_dm_find_first_crtc_matching_connector(state, crtc),
|
||||
connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_first_crtc_returns_first - Test find_first_crtc returns the first match
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_first_crtc_returns_first(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_commit *state = dm_test_alloc_atomic_state(test, 2);
|
||||
struct drm_connector *first;
|
||||
struct drm_connector *second;
|
||||
struct drm_connector_state *first_state;
|
||||
struct drm_connector_state *second_state;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
first = kunit_kzalloc(test, sizeof(*first), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, first);
|
||||
second = kunit_kzalloc(test, sizeof(*second), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, second);
|
||||
first_state = kunit_kzalloc(test, sizeof(*first_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, first_state);
|
||||
second_state = kunit_kzalloc(test, sizeof(*second_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, second_state);
|
||||
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, crtc);
|
||||
|
||||
/* Both connectors target the same crtc; the first one must win. */
|
||||
first_state->crtc = crtc;
|
||||
second_state->crtc = crtc;
|
||||
state->connectors[0].ptr = first;
|
||||
state->connectors[0].new_state = first_state;
|
||||
state->connectors[1].ptr = second;
|
||||
state->connectors[1].new_state = second_state;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test,
|
||||
amdgpu_dm_find_first_crtc_matching_connector(state, crtc),
|
||||
first);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_set_panel_type() */
|
||||
|
||||
/*
|
||||
* Build an amdgpu_dm_connector registered against a real kunit drm_device that
|
||||
* is embedded in an amdgpu_device, so drm_to_adev()/adev_to_drm() resolve and
|
||||
* the panel_type property can be created, attached and updated for real.
|
||||
*/
|
||||
struct dm_test_panel_ctx {
|
||||
struct amdgpu_device *adev;
|
||||
struct drm_device *drm;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_link *link;
|
||||
struct drm_display_info *display_info;
|
||||
};
|
||||
|
||||
static struct dm_test_panel_ctx *dm_test_panel_ctx_alloc(struct kunit *test)
|
||||
{
|
||||
struct dm_test_panel_ctx *ctx;
|
||||
struct drm_property *prop;
|
||||
struct device *dev;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx);
|
||||
|
||||
dev = drm_kunit_helper_alloc_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
|
||||
|
||||
ctx->drm = __drm_kunit_helper_alloc_drm_device(test, dev,
|
||||
sizeof(*ctx->adev),
|
||||
offsetof(struct amdgpu_device, ddev),
|
||||
DRIVER_MODESET);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm);
|
||||
ctx->adev = drm_to_adev(ctx->drm);
|
||||
|
||||
/* The function under test writes through this property. */
|
||||
prop = drm_property_create_range(ctx->drm, DRM_MODE_PROP_IMMUTABLE,
|
||||
"panel_type", 0, 0xff);
|
||||
KUNIT_ASSERT_NOT_NULL(test, prop);
|
||||
ctx->drm->mode_config.panel_type_property = prop;
|
||||
|
||||
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
|
||||
KUNIT_ASSERT_EQ(test,
|
||||
drmm_connector_init(ctx->drm, &ctx->aconnector->base,
|
||||
&dm_test_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_eDP, NULL), 0);
|
||||
drm_object_attach_property(&ctx->aconnector->base.base, prop,
|
||||
DRM_MODE_PANEL_TYPE_UNKNOWN);
|
||||
|
||||
ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx->link);
|
||||
ctx->aconnector->dc_link = ctx->link;
|
||||
|
||||
ctx->display_info = &ctx->aconnector->base.display_info;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static uint64_t dm_test_panel_prop_value(struct kunit *test,
|
||||
struct dm_test_panel_ctx *ctx)
|
||||
{
|
||||
uint64_t val = ~0ULL;
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
drm_object_property_get_value(&ctx->aconnector->base.base,
|
||||
ctx->drm->mode_config.panel_type_property, &val), 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_set_panel_type_samsung_miniled - Test Samsung luminance heuristic
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When no VSDB or DPCD hint is present, a Samsung sink whose first luminance
|
||||
* range is at least 1.5x the second is treated as mini-LED.
|
||||
*/
|
||||
static void dm_test_set_panel_type_samsung_miniled(struct kunit *test)
|
||||
{
|
||||
struct dm_test_panel_ctx *ctx = dm_test_panel_ctx_alloc(test);
|
||||
struct dc_sink *sink;
|
||||
|
||||
sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, sink);
|
||||
sink->edid_caps.manufacturer_id = DDC_MANUFACTURERNAME_SAMSUNG;
|
||||
ctx->link->local_sink = sink;
|
||||
|
||||
ctx->display_info->amd_vsdb.version = 1;
|
||||
ctx->display_info->amd_vsdb.luminance_range1.max_luminance = 1500;
|
||||
ctx->display_info->amd_vsdb.luminance_range2.max_luminance = 1000;
|
||||
|
||||
amdgpu_dm_set_panel_type(ctx->aconnector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)ctx->link->panel_type, (int)PANEL_TYPE_MINILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_set_panel_type_samsung_below_threshold - Test Samsung sink below the
|
||||
* mini-LED luminance threshold falls back to LCD
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_set_panel_type_samsung_below_threshold(struct kunit *test)
|
||||
{
|
||||
struct dm_test_panel_ctx *ctx = dm_test_panel_ctx_alloc(test);
|
||||
struct dc_sink *sink;
|
||||
|
||||
sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, sink);
|
||||
sink->edid_caps.manufacturer_id = DDC_MANUFACTURERNAME_SAMSUNG;
|
||||
ctx->link->local_sink = sink;
|
||||
|
||||
ctx->display_info->amd_vsdb.version = 1;
|
||||
ctx->display_info->amd_vsdb.luminance_range1.max_luminance = 1000;
|
||||
ctx->display_info->amd_vsdb.luminance_range2.max_luminance = 1000;
|
||||
|
||||
amdgpu_dm_set_panel_type(ctx->aconnector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)ctx->link->panel_type, (int)PANEL_TYPE_LCD);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_set_panel_type_default_lcd - Test default fallback is LCD
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With no VSDB, DPCD or DID hints the panel type defaults to LCD.
|
||||
*/
|
||||
static void dm_test_set_panel_type_default_lcd(struct kunit *test)
|
||||
{
|
||||
struct dm_test_panel_ctx *ctx = dm_test_panel_ctx_alloc(test);
|
||||
|
||||
amdgpu_dm_set_panel_type(ctx->aconnector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)ctx->link->panel_type, (int)PANEL_TYPE_LCD);
|
||||
KUNIT_EXPECT_EQ(test, dm_test_panel_prop_value(test, ctx),
|
||||
(uint64_t)DRM_MODE_PANEL_TYPE_LCD);
|
||||
}
|
||||
|
||||
static struct kunit_case amdgpu_dm_connector_tests[] = {
|
||||
/* get_subconnector_type */
|
||||
KUNIT_CASE(dm_test_subconnector_type_none),
|
||||
|
|
@ -3063,6 +3366,16 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
|
|||
KUNIT_CASE(dm_test_detect_mst_skips_writeback),
|
||||
KUNIT_CASE(dm_test_detect_mst_non_mst_link),
|
||||
KUNIT_CASE(dm_test_detect_mst_branch_without_aux),
|
||||
/* amdgpu_dm_find_first_crtc_matching_connector */
|
||||
KUNIT_CASE(dm_test_find_first_crtc_match),
|
||||
KUNIT_CASE(dm_test_find_first_crtc_no_match),
|
||||
KUNIT_CASE(dm_test_find_first_crtc_empty_state),
|
||||
KUNIT_CASE(dm_test_find_first_crtc_skips_null_ptr),
|
||||
KUNIT_CASE(dm_test_find_first_crtc_returns_first),
|
||||
/* amdgpu_dm_set_panel_type */
|
||||
KUNIT_CASE(dm_test_set_panel_type_samsung_miniled),
|
||||
KUNIT_CASE(dm_test_set_panel_type_samsung_below_threshold),
|
||||
KUNIT_CASE(dm_test_set_panel_type_default_lcd),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user