From 2e245c3c9dd13211d410ca486bb0bd750d2d749d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 17 Sep 2026 11:31:59 -0400 Subject: [PATCH 001/114] drm/amd/display: fix merge fallout in KUNIT tests Drop no longer valid tests and fix function signature. Signed-off-by: Alex Deucher --- .../tests/amdgpu_dm_connector_test.c | 337 ------------------ .../display/amdgpu_dm/tests/amdgpu_dm_test.c | 3 +- 2 files changed, 2 insertions(+), 338 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index 8107be27d324..7b6400ec8107 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -6134,331 +6134,6 @@ static void dm_test_restore_state_same_sink(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, stream->sink, aconnector->dc_sink); } -/* Mock DMCU plumbing for parse_edid_cea_dmcu() tests. */ -static bool dm_test_dmcu_is_init(struct dmcu *dmcu) -{ - return true; -} - -static bool dm_test_dmcu_send_cea(struct dmcu *dmcu, int offset, - int total_length, u8 *data, int length) -{ - return true; -} - -static bool dm_test_dmcu_recv_ack_ok(struct dmcu *dmcu, int *offset) -{ - *offset = 0; - return true; -} - -static bool dm_test_dmcu_recv_ack_fail(struct dmcu *dmcu, int *offset) -{ - return false; -} - -static bool dm_test_dmcu_recv_vsdb_found(struct dmcu *dmcu, int *version, - int *min_frame_rate, int *max_frame_rate) -{ - *version = 2; - *min_frame_rate = 24; - *max_frame_rate = 60; - return true; -} - -static bool dm_test_dmcu_recv_vsdb_none(struct dmcu *dmcu, int *version, - int *min_frame_rate, int *max_frame_rate) -{ - return false; -} - -static const struct dmcu_funcs dm_test_dmcu_funcs_vsdb = { - .is_dmcu_initialized = dm_test_dmcu_is_init, - .send_edid_cea = dm_test_dmcu_send_cea, - .recv_edid_cea_ack = dm_test_dmcu_recv_ack_ok, - .recv_amd_vsdb = dm_test_dmcu_recv_vsdb_found, -}; - -static const struct dmcu_funcs dm_test_dmcu_funcs_novsdb = { - .is_dmcu_initialized = dm_test_dmcu_is_init, - .send_edid_cea = dm_test_dmcu_send_cea, - .recv_edid_cea_ack = dm_test_dmcu_recv_ack_ok, - .recv_amd_vsdb = dm_test_dmcu_recv_vsdb_none, -}; - -static const struct dmcu_funcs dm_test_dmcu_funcs_ackfail = { - .is_dmcu_initialized = dm_test_dmcu_is_init, - .send_edid_cea = dm_test_dmcu_send_cea, - .recv_edid_cea_ack = dm_test_dmcu_recv_ack_fail, - .recv_amd_vsdb = dm_test_dmcu_recv_vsdb_none, -}; - -/* - * Build a bare display manager carrying a dc with a resource pool whose DMCU - * uses the supplied funcs table (or no DMCU at all when @funcs is NULL). The - * DMCU CEA parser path only touches dm->dc->res_pool->dmcu, so no adev/ctx is - * required. - */ -static struct amdgpu_display_manager * -dm_test_alloc_dm_dmcu(struct kunit *test, const struct dmcu_funcs *funcs) -{ - struct amdgpu_display_manager *dm; - struct resource_pool *pool; - struct dmcu *dmcu = NULL; - struct dc *dc; - - dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, dm); - dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, dc); - pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, pool); - - if (funcs) { - dmcu = kunit_kzalloc(test, sizeof(*dmcu), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, dmcu); - dmcu->funcs = funcs; - } - - pool->dmcu = dmcu; - dc->res_pool = pool; - dm->dc = dc; - return dm; -} - -/* - * Build a display manager backed by an amdgpu_device and a dc with a ctx (but - * no DMUB), so the DMUB CEA send path can build a command, fail the execute, - * and log via drm_err() safely. - */ -static struct amdgpu_display_manager *dm_test_alloc_dm_adev(struct kunit *test) -{ - struct amdgpu_device *adev = dm_kunit_alloc_adev(test); - struct dc *dc = dm_kunit_alloc_dc_with_ctx(test); - - KUNIT_ASSERT_NOT_NULL(test, adev); - KUNIT_ASSERT_NOT_NULL(test, dc); - adev->dm.adev = adev; - adev->dm.dc = dc; - return &adev->dm; -} - -/** - * dm_test_send_cea_length_too_long - Test an oversized chunk is rejected - * @test: KUnit test context - */ -static void dm_test_send_cea_length_too_long(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_adev(test); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 data[16] = {0}; - bool ret; - - ret = dm_edid_parser_send_cea(dm, 0, 128, data, - DMUB_EDID_CEA_DATA_CHUNK_BYTES + 1, &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_send_cea_dmub_unavailable - Test a failed DMUB command reports false - * @test: KUnit test context - */ -static void dm_test_send_cea_dmub_unavailable(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_adev(test); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 data[DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - /* ctx->dmub_srv is NULL, so the DMUB command execute fails. */ - ret = dm_edid_parser_send_cea(dm, 0, 8, data, - DMUB_EDID_CEA_DATA_CHUNK_BYTES, &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_parse_cea_dmcu_empty - Test an empty extension parses to no VSDB - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_empty(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_dmcu(test, NULL); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[1] = {0}; - - KUNIT_EXPECT_FALSE(test, parse_edid_cea_dmcu(dm, ext, 0, &vsdb)); -} - -/** - * dm_test_parse_cea_dmcu_no_dmcu - Test a missing DMCU fails the first send - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_no_dmcu(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_dmcu(test, NULL); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - /* res_pool->dmcu is NULL, so the CEA send returns false. */ - ret = parse_edid_cea_dmcu(dm, ext, DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_parse_cea_dmcu_vsdb_found - Test the DMCU reports an AMD VSDB - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_vsdb_found(struct kunit *test) -{ - struct amdgpu_display_manager *dm = - dm_test_alloc_dm_dmcu(test, &dm_test_dmcu_funcs_vsdb); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - ret = parse_edid_cea_dmcu(dm, ext, DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_TRUE(test, ret); - KUNIT_EXPECT_TRUE(test, vsdb.freesync_supported); - KUNIT_EXPECT_EQ(test, vsdb.amd_vsdb_version, 2); - KUNIT_EXPECT_EQ(test, vsdb.min_refresh_rate_hz, 24); - KUNIT_EXPECT_EQ(test, vsdb.max_refresh_rate_hz, 60); - KUNIT_EXPECT_EQ(test, vsdb.freesync_mccs_vcp_code, 0); -} - -/** - * dm_test_parse_cea_dmcu_vsdb_none - Test the DMCU finds no AMD VSDB - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_vsdb_none(struct kunit *test) -{ - struct amdgpu_display_manager *dm = - dm_test_alloc_dm_dmcu(test, &dm_test_dmcu_funcs_novsdb); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - ret = parse_edid_cea_dmcu(dm, ext, DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_parse_cea_dmcu_multi_chunk - Test intermediate chunks are acked - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_multi_chunk(struct kunit *test) -{ - struct amdgpu_display_manager *dm = - dm_test_alloc_dm_dmcu(test, &dm_test_dmcu_funcs_vsdb); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[2 * DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - ret = parse_edid_cea_dmcu(dm, ext, 2 * DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_TRUE(test, ret); -} - -/** - * dm_test_parse_cea_dmcu_ack_fail - Test a failed chunk ack aborts the parse - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmcu_ack_fail(struct kunit *test) -{ - struct amdgpu_display_manager *dm = - dm_test_alloc_dm_dmcu(test, &dm_test_dmcu_funcs_ackfail); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[2 * DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - ret = parse_edid_cea_dmcu(dm, ext, 2 * DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_parse_cea_dmub_empty - Test an empty extension returns the flag - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmub_empty(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_adev(test); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[1] = {0}; - - /* No chunks are sent, so the freesync flag is returned as-is. */ - vsdb.freesync_supported = true; - KUNIT_EXPECT_TRUE(test, parse_edid_cea_dmub(dm, ext, 0, &vsdb)); -} - -/** - * dm_test_parse_cea_dmub_send_fail - Test a chunk send failure aborts the parse - * @test: KUnit test context - */ -static void dm_test_parse_cea_dmub_send_fail(struct kunit *test) -{ - struct amdgpu_display_manager *dm = dm_test_alloc_dm_adev(test); - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[DMUB_EDID_CEA_DATA_CHUNK_BYTES] = {0}; - bool ret; - - /* The first chunk send fails (no DMUB), so the parse bails out. */ - ret = parse_edid_cea_dmub(dm, ext, DMUB_EDID_CEA_DATA_CHUNK_BYTES, - &vsdb); - KUNIT_EXPECT_FALSE(test, ret); -} - -/** - * dm_test_parse_cea_routes_dmub - Test a present dmub_srv routes to DMUB - * @test: KUnit test context - */ -static void dm_test_parse_cea_routes_dmub(struct kunit *test) -{ - struct amdgpu_device *adev = dm_kunit_alloc_adev(test); - struct amdgpu_dm_connector *aconnector; - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[1] = {0}; - - KUNIT_ASSERT_NOT_NULL(test, adev); - mutex_init(&adev->dm.dc_lock); - adev->dm.dmub_srv = kunit_kzalloc(test, 1, GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, adev->dm.dmub_srv); - - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, aconnector); - aconnector->base.dev = &adev->ddev; - - /* len 0 routes to the DMUB parser, which returns the freesync flag. */ - vsdb.freesync_supported = true; - KUNIT_EXPECT_TRUE(test, parse_edid_cea(aconnector, ext, 0, &vsdb)); -} - -/** - * dm_test_parse_cea_routes_dmcu - Test a missing dmub_srv routes to DMCU - * @test: KUnit test context - */ -static void dm_test_parse_cea_routes_dmcu(struct kunit *test) -{ - struct amdgpu_device *adev = dm_kunit_alloc_adev(test); - struct amdgpu_dm_connector *aconnector; - struct amdgpu_hdmi_vsdb_info vsdb = {0}; - u8 ext[1] = {0}; - - KUNIT_ASSERT_NOT_NULL(test, adev); - mutex_init(&adev->dm.dc_lock); - adev->dm.dmub_srv = NULL; - - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, aconnector); - aconnector->base.dev = &adev->ddev; - - /* len 0 routes to the DMCU parser, which returns false. */ - KUNIT_EXPECT_FALSE(test, parse_edid_cea(aconnector, ext, 0, &vsdb)); -} - /** * dm_test_get_amd_vsdb_unsupported - Test a zero VSDB version reports no support * @test: The KUnit test context @@ -9155,18 +8830,6 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_restore_state_no_encoder), KUNIT_CASE(dm_test_restore_state_no_stream), KUNIT_CASE(dm_test_restore_state_same_sink), - KUNIT_CASE(dm_test_send_cea_length_too_long), - KUNIT_CASE(dm_test_send_cea_dmub_unavailable), - KUNIT_CASE(dm_test_parse_cea_dmcu_empty), - KUNIT_CASE(dm_test_parse_cea_dmcu_no_dmcu), - KUNIT_CASE(dm_test_parse_cea_dmcu_vsdb_found), - KUNIT_CASE(dm_test_parse_cea_dmcu_vsdb_none), - KUNIT_CASE(dm_test_parse_cea_dmcu_multi_chunk), - KUNIT_CASE(dm_test_parse_cea_dmcu_ack_fail), - KUNIT_CASE(dm_test_parse_cea_dmub_empty), - KUNIT_CASE(dm_test_parse_cea_dmub_send_fail), - KUNIT_CASE(dm_test_parse_cea_routes_dmub), - KUNIT_CASE(dm_test_parse_cea_routes_dmcu), /* get_amd_vsdb */ KUNIT_CASE(dm_test_get_amd_vsdb_unsupported), KUNIT_CASE(dm_test_get_amd_vsdb_supported), diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c index 09922e65fb16..48bcd6541083 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -3839,7 +3839,8 @@ dm_test_plane_info_ctx_alloc(struct kunit *test, struct amdgpu_device *adev, static int dm_test_fill_plane_info(struct dm_test_plane_info_ctx *ctx) { - return fill_dc_plane_info_and_addr(ctx->adev, ctx->plane_state, + return fill_dc_plane_info_and_addr(ctx->adev, ctx->plane_state->state, + ctx->plane_state, &ctx->plane_info, &ctx->address, false); } From cbdb859f65745ddf291d5589fcd195b0f10f9b9a Mon Sep 17 00:00:00 2001 From: Tomasz Siemek Date: Mon, 31 Aug 2026 11:35:54 +0200 Subject: [PATCH 002/114] drm/amd/display: Decouple cursor offload hwss executors from pipe context [Why] Hwss executors shouldn't be coupled to complex dc structs such as pipe_ctx. [How] Refactor cursor offload hardware sequencer extension to accept only the parameters required for programming cursor offload state. Extract these parameters in the generic HWSS layer for both immediate execution and block-sequence construction. Reviewed-by: Ilya Bakoulin Signed-off-by: Tomasz Siemek Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/core/dc_hw_sequencer.c | 211 ++++++++++-------- .../gpu/drm/amd/display/dc/core/dc_stream.c | 58 ++++- .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 10 +- .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 10 +- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 67 +++--- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.h | 10 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 18 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 3 +- .../amd/display/dc/hwss/dcn50/dcn50_hwseq.c | 10 +- .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 18 +- .../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 3 +- .../drm/amd/display/dc/hwss/hw_sequencer.h | 56 +++-- 12 files changed, 274 insertions(+), 200 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index b54466e4f42b..4b8af67ff4b5 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -1319,11 +1319,7 @@ void hwss_build_fast_sequence(struct dc *dc, cursor_pipe_to_program = current_pipe; if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { - block_sequence[*num_steps].params.begin_cursor_offload_update_params.dc = dc; - block_sequence[*num_steps].params.begin_cursor_offload_update_params.pipe_ctx = - current_pipe; - block_sequence[*num_steps].func = HWSS_BEGIN_CURSOR_OFFLOAD_UPDATE; - (*num_steps)++; + hwss_add_begin_cursor_offload_update(&seq_state, dc, current_pipe); } else { block_sequence[*num_steps].params.cursor_lock_params.dc = dc; block_sequence[*num_steps].params.cursor_lock_params.pipe_ctx = current_pipe; @@ -1364,22 +1360,14 @@ void hwss_build_fast_sequence(struct dc *dc, hwss_add_set_cursor_sdr_white_level(&seq_state, current_pipe); if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) { - block_sequence[*num_steps].params.update_cursor_offload_pipe_params.dc = dc; - block_sequence[*num_steps].params.update_cursor_offload_pipe_params.pipe_ctx = - current_pipe; - block_sequence[*num_steps].func = HWSS_UPDATE_CURSOR_OFFLOAD_PIPE; - (*num_steps)++; + hwss_add_update_cursor_offload_pipe(&seq_state, dc, current_pipe); } } /* Unlock cursor attributes after all pipes have been programmed */ if (cursor_pipe_to_program) { if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { - block_sequence[*num_steps].params.commit_cursor_offload_update_params.dc = dc; - block_sequence[*num_steps].params.commit_cursor_offload_update_params.pipe_ctx = - cursor_pipe_to_program; - block_sequence[*num_steps].func = HWSS_COMMIT_CURSOR_OFFLOAD_UPDATE; - (*num_steps)++; + hwss_add_commit_cursor_offload_update(&seq_state, dc, cursor_pipe_to_program); } else { block_sequence[*num_steps].params.cursor_lock_params.dc = dc; block_sequence[*num_steps].params.cursor_lock_params.pipe_ctx = cursor_pipe_to_program; @@ -1417,11 +1405,7 @@ void hwss_build_fast_sequence(struct dc *dc, cursor_pipe_to_program = current_pipe; if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { - block_sequence[*num_steps].params.begin_cursor_offload_update_params.dc = dc; - block_sequence[*num_steps].params.begin_cursor_offload_update_params.pipe_ctx = - current_pipe; - block_sequence[*num_steps].func = HWSS_BEGIN_CURSOR_OFFLOAD_UPDATE; - (*num_steps)++; + hwss_add_begin_cursor_offload_update(&seq_state, dc, current_pipe); } else { block_sequence[*num_steps].params.cursor_lock_params.dc = dc; block_sequence[*num_steps].params.cursor_lock_params.pipe_ctx = current_pipe; @@ -1437,11 +1421,7 @@ void hwss_build_fast_sequence(struct dc *dc, (*num_steps)++; if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) { - block_sequence[*num_steps].params.update_cursor_offload_pipe_params.dc = dc; - block_sequence[*num_steps].params.update_cursor_offload_pipe_params.pipe_ctx = - current_pipe; - block_sequence[*num_steps].func = HWSS_UPDATE_CURSOR_OFFLOAD_PIPE; - (*num_steps)++; + hwss_add_update_cursor_offload_pipe(&seq_state, dc, current_pipe); } if (dc->ctx->dmub_srv) @@ -1453,11 +1433,7 @@ void hwss_build_fast_sequence(struct dc *dc, /* Unlock cursor position after all pipes have been programmed */ if (cursor_pipe_to_program) { if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { - block_sequence[*num_steps].params.commit_cursor_offload_update_params.dc = dc; - block_sequence[*num_steps].params.commit_cursor_offload_update_params.pipe_ctx = - cursor_pipe_to_program; - block_sequence[*num_steps].func = HWSS_COMMIT_CURSOR_OFFLOAD_UPDATE; - (*num_steps)++; + hwss_add_commit_cursor_offload_update(&seq_state, dc, cursor_pipe_to_program); } else { block_sequence[*num_steps].params.cursor_lock_params.dc = dc; block_sequence[*num_steps].params.cursor_lock_params.pipe_ctx = cursor_pipe_to_program; @@ -2127,20 +2103,20 @@ void hwss_execute_sequence(struct dc *dc, case HUBP_MEM_PROGRAM_VIEWPORT: hwss_hubp_mem_program_viewport(params); break; - case ABORT_CURSOR_OFFLOAD_UPDATE: - hwss_abort_cursor_offload_update(params); - break; case HWSS_CURSOR_LOCK: hwss_cursor_lock(params); break; case HWSS_BEGIN_CURSOR_OFFLOAD_UPDATE: - hwss_begin_cursor_offload_update(params); + hwss_begin_cursor_offload_update(dc, params); break; case HWSS_COMMIT_CURSOR_OFFLOAD_UPDATE: - hwss_commit_cursor_offload_update(params); + hwss_commit_cursor_offload_update(dc, params); break; case HWSS_UPDATE_CURSOR_OFFLOAD_PIPE: - hwss_update_cursor_offload_pipe(params); + hwss_update_cursor_offload_pipe(dc, params); + break; + case ABORT_CURSOR_OFFLOAD_UPDATE: + hwss_abort_cursor_offload_update(dc, params); break; case DC_SEND_CURSOR_INFO_TO_DMU: hwss_send_cursor_info_to_dmu(params); @@ -4321,15 +4297,6 @@ void hwss_hubp_mem_program_viewport(union block_sequence_params *params) hubp->funcs->mem_program_viewport(hubp, viewport, viewport_c); } -void hwss_abort_cursor_offload_update(union block_sequence_params *params) -{ - struct dc *dc = params->abort_cursor_offload_update_params.dc; - struct pipe_ctx *pipe_ctx = params->abort_cursor_offload_update_params.pipe_ctx; - - if (dc && dc->hwss.abort_cursor_offload_update) - dc->hwss.abort_cursor_offload_update(dc, pipe_ctx); -} - void hwss_cursor_lock(union block_sequence_params *params) { struct dc *dc = params->cursor_lock_params.dc; @@ -4340,31 +4307,45 @@ void hwss_cursor_lock(union block_sequence_params *params) dc->hwss.cursor_lock(dc, pipe_ctx, lock); } -void hwss_begin_cursor_offload_update(union block_sequence_params *params) +void hwss_begin_cursor_offload_update(struct dc *dc, union block_sequence_params *params) { - struct dc *dc = params->begin_cursor_offload_update_params.dc; - struct pipe_ctx *pipe_ctx = params->begin_cursor_offload_update_params.pipe_ctx; - - if (dc && dc->hwss.begin_cursor_offload_update) - dc->hwss.begin_cursor_offload_update(dc, pipe_ctx); + if (dc->hwss.begin_cursor_offload_update) + dc->hwss.begin_cursor_offload_update( + params->begin_cursor_offload_update_params.dmub, + params->begin_cursor_offload_update_params.dpp, + params->begin_cursor_offload_update_params.hubp, + params->begin_cursor_offload_update_params.stream_idx); } -void hwss_commit_cursor_offload_update(union block_sequence_params *params) +void hwss_commit_cursor_offload_update(struct dc *dc, union block_sequence_params *params) { - struct dc *dc = params->commit_cursor_offload_update_params.dc; - struct pipe_ctx *pipe_ctx = params->commit_cursor_offload_update_params.pipe_ctx; - - if (dc && dc->hwss.commit_cursor_offload_update) - dc->hwss.commit_cursor_offload_update(dc, pipe_ctx); + if (dc->hwss.commit_cursor_offload_update) + dc->hwss.commit_cursor_offload_update( + params->commit_cursor_offload_update_params.dmub, + params->commit_cursor_offload_update_params.dpp, + params->commit_cursor_offload_update_params.hubp, + params->commit_cursor_offload_update_params.stream_idx); } -void hwss_update_cursor_offload_pipe(union block_sequence_params *params) +void hwss_update_cursor_offload_pipe(struct dc *dc, union block_sequence_params *params) { - struct dc *dc = params->update_cursor_offload_pipe_params.dc; - struct pipe_ctx *pipe_ctx = params->update_cursor_offload_pipe_params.pipe_ctx; + if (dc->hwss.update_cursor_offload_pipe) + dc->hwss.update_cursor_offload_pipe( + params->update_cursor_offload_pipe_params.dmub, + params->update_cursor_offload_pipe_params.stream_idx, + params->update_cursor_offload_pipe_params.pipe_idx, + params->update_cursor_offload_pipe_params.dpp, + params->update_cursor_offload_pipe_params.hubp); +} - if (dc && dc->hwss.update_cursor_offload_pipe) - dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx); +void hwss_abort_cursor_offload_update(struct dc *dc, union block_sequence_params *params) +{ + if (dc->hwss.abort_cursor_offload_update) + dc->hwss.abort_cursor_offload_update( + params->abort_cursor_offload_update_params.dmub, + params->abort_cursor_offload_update_params.dpp, + params->abort_cursor_offload_update_params.hubp, + params->abort_cursor_offload_update_params.stream_idx); } void hwss_send_cursor_info_to_dmu(union block_sequence_params *params) @@ -5755,18 +5736,6 @@ void hwss_add_hubp_mem_program_viewport(struct block_sequence_state *seq_state, } } -void hwss_add_abort_cursor_offload_update(struct block_sequence_state *seq_state, - struct dc *dc, - struct pipe_ctx *pipe_ctx) -{ - if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { - seq_state->steps[*seq_state->num_steps].func = ABORT_CURSOR_OFFLOAD_UPDATE; - seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.pipe_ctx = pipe_ctx; - (*seq_state->num_steps)++; - } -} - void hwss_add_set_cursor_attribute(struct block_sequence_state *seq_state, struct dc *dc, struct pipe_ctx *pipe_ctx) @@ -5907,18 +5876,6 @@ void hwss_add_tg_get_frame_count(struct block_sequence_state *seq_state, } } -void hwss_add_begin_cursor_offload_update(struct block_sequence_state *seq_state, - struct dc *dc, - struct pipe_ctx *pipe_ctx) -{ - if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { - seq_state->steps[*seq_state->num_steps].func = HWSS_BEGIN_CURSOR_OFFLOAD_UPDATE; - seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.pipe_ctx = pipe_ctx; - (*seq_state->num_steps)++; - } -} - void hwss_add_cursor_lock(struct block_sequence_state *seq_state, struct dc *dc, struct pipe_ctx *pipe_ctx, @@ -5967,10 +5924,27 @@ void hwss_add_update_cursor_offload_pipe(struct block_sequence_state *seq_state, struct dc *dc, struct pipe_ctx *pipe_ctx) { + const struct pipe_ctx *top_pipe; + + if (!dc->hwss.update_cursor_offload_pipe) + return; + + top_pipe = resource_get_otg_master(pipe_ctx); + if (!top_pipe) + return; + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { seq_state->steps[*seq_state->num_steps].func = HWSS_UPDATE_CURSOR_OFFLOAD_PIPE; - seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.pipe_ctx = pipe_ctx; + seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.dmub = + dc->ctx->dmub_srv->dmub; + seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.stream_idx = + top_pipe->pipe_idx; + seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.pipe_idx = + pipe_ctx->pipe_idx; + seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.dpp = + pipe_ctx->plane_res.dpp; + seq_state->steps[*seq_state->num_steps].params.update_cursor_offload_pipe_params.hubp = + pipe_ctx->plane_res.hubp; (*seq_state->num_steps)++; } } @@ -5979,10 +5953,67 @@ void hwss_add_commit_cursor_offload_update(struct block_sequence_state *seq_stat struct dc *dc, struct pipe_ctx *pipe_ctx) { + const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (!top_pipe) + return; + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { seq_state->steps[*seq_state->num_steps].func = HWSS_COMMIT_CURSOR_OFFLOAD_UPDATE; - seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.pipe_ctx = pipe_ctx; + seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.dmub = + dc->ctx->dmub_srv->dmub; + seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.stream_idx = + top_pipe->pipe_idx; + seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.dpp = + pipe_ctx->plane_res.dpp; + seq_state->steps[*seq_state->num_steps].params.commit_cursor_offload_update_params.hubp = + pipe_ctx->plane_res.hubp; + (*seq_state->num_steps)++; + } +} + +void hwss_add_begin_cursor_offload_update(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (!top_pipe) + return; + + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HWSS_BEGIN_CURSOR_OFFLOAD_UPDATE; + seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.dmub = + dc->ctx->dmub_srv->dmub; + seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.stream_idx = + top_pipe->pipe_idx; + seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.dpp = + pipe_ctx->plane_res.dpp; + seq_state->steps[*seq_state->num_steps].params.begin_cursor_offload_update_params.hubp = + pipe_ctx->plane_res.hubp; + (*seq_state->num_steps)++; + } +} + +void hwss_add_abort_cursor_offload_update(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (!top_pipe) + return; + + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = ABORT_CURSOR_OFFLOAD_UPDATE; + seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.dmub = + dc->ctx->dmub_srv->dmub; + seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.stream_idx = + top_pipe->pipe_idx; + seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.dpp = + pipe_ctx->plane_res.dpp; + seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.hubp = + pipe_ctx->plane_res.hubp; (*seq_state->num_steps)++; } } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index 35d4d0d5ae34..c05428f52d88 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -323,7 +323,12 @@ void program_cursor_attributes( pipe_to_program = pipe_ctx; if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { - dc->hwss.begin_cursor_offload_update(dc, pipe_ctx); + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.begin_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp, + top_pipe->pipe_idx); } else { dc->hwss.cursor_lock(dc, pipe_to_program, true); if (pipe_to_program->next_odm_pipe) @@ -335,13 +340,24 @@ void program_cursor_attributes( update_cursor_info_to_dmu(dc, pipe_ctx); if (dc->hwss.set_cursor_sdr_white_level) dc->hwss.set_cursor_sdr_white_level(pipe_ctx); - if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) - dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx); + if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.update_cursor_offload_pipe(dc->ctx->dmub_srv->dmub, + top_pipe->pipe_idx, pipe_ctx->pipe_idx, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp); + } } if (pipe_to_program) { if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { - dc->hwss.commit_cursor_offload_update(dc, pipe_to_program); + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_to_program); + + if (top_pipe) + dc->hwss.commit_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_to_program->plane_res.dpp, pipe_to_program->plane_res.hubp, + top_pipe->pipe_idx); } else { dc->hwss.cursor_lock(dc, pipe_to_program, false); if (pipe_to_program->next_odm_pipe) @@ -484,24 +500,42 @@ void program_cursor_position( if (!pipe_to_program) { pipe_to_program = pipe_ctx; - if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) - dc->hwss.begin_cursor_offload_update(dc, pipe_ctx); - else + if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.begin_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp, + top_pipe->pipe_idx); + } else { dc->hwss.cursor_lock(dc, pipe_to_program, true); + } } dc->hwss.set_cursor_position(pipe_ctx); - if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) - dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx); + if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.update_cursor_offload_pipe(dc->ctx->dmub_srv->dmub, + top_pipe->pipe_idx, pipe_ctx->pipe_idx, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp); + } update_cursor_info_to_dmu(dc, pipe_ctx); } if (pipe_to_program) { - if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) - dc->hwss.commit_cursor_offload_update(dc, pipe_to_program); - else + if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_to_program); + + if (top_pipe) + dc->hwss.commit_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_to_program->plane_res.dpp, pipe_to_program->plane_res.hubp, + top_pipe->pipe_idx); + } else { dc->hwss.cursor_lock(dc, pipe_to_program, false); + } } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 1b0fc0853045..5a163711498a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -3145,8 +3145,14 @@ static void dcn10_update_dchubp_dpp( } if (pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { - if (dc->hwss.abort_cursor_offload_update) - dc->hwss.abort_cursor_offload_update(dc, pipe_ctx); + if (dc_dmub_srv_is_cursor_offload_enabled(dc) && dc->hwss.abort_cursor_offload_update) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.abort_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp, + top_pipe->pipe_idx); + } dc->hwss.set_cursor_attribute(pipe_ctx); dc->hwss.set_cursor_position(pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 1e60b852e61f..4e7a005d27ef 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -1834,8 +1834,14 @@ void dcn20_update_dchubp_dpp( if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || pipe_ctx->update_flags.bits.scaler || viewport_changed == true) && pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { - if (dc->hwss.abort_cursor_offload_update) - dc->hwss.abort_cursor_offload_update(dc, pipe_ctx); + if (dc_dmub_srv_is_cursor_offload_enabled(dc) && dc->hwss.abort_cursor_offload_update) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.abort_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp, + top_pipe->pipe_idx); + } dc->hwss.set_cursor_attribute(pipe_ctx); dc->hwss.set_cursor_position(pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 6e1079377f48..16526c66c2d1 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -1642,10 +1642,9 @@ void dcn35_hardware_release(struct dc *dc) dc->hwss.hw_block_power_up(dc, &pg_update_state); } -void dcn35_abort_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe) +void dcn35_abort_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, struct hubp *hubp, uint32_t stream_idx) { - if (!dc_dmub_srv_is_cursor_offload_enabled(dc)) - return; + struct dc *dc = dpp->ctx->dc; /* * Insert a blank update to modify the write index and set pipe_mask to 0. @@ -1664,56 +1663,46 @@ void dcn35_abort_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pip */ if (dc->hwss.begin_cursor_offload_update) - dc->hwss.begin_cursor_offload_update(dc, pipe); + dc->hwss.begin_cursor_offload_update(dmub, dpp, hubp, stream_idx); if (dc->hwss.commit_cursor_offload_update) - dc->hwss.commit_cursor_offload_update(dc, pipe); + dc->hwss.commit_cursor_offload_update(dmub, dpp, hubp, stream_idx); } -void dcn35_begin_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe) +void dcn35_begin_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, struct hubp *hubp, uint32_t stream_idx) { - volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; - const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); - uint32_t stream_idx, write_idx, payload_idx; + volatile struct dmub_cursor_offload_v1 *cs = dmub->cursor_offload_v1; + uint32_t write_idx, payload_idx; - if (!top_pipe) - return; - - stream_idx = top_pipe->pipe_idx; write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); cs->offload_streams[stream_idx].payloads[payload_idx].write_idx_start = write_idx; cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask = 0; - if (pipe->plane_res.hubp) - pipe->plane_res.hubp->cursor_offload = true; + if (hubp) + hubp->cursor_offload = true; - if (pipe->plane_res.dpp) - pipe->plane_res.dpp->cursor_offload = true; + if (dpp) + dpp->cursor_offload = true; } -void dcn35_commit_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe) +void dcn35_commit_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, struct hubp *hubp, uint32_t stream_idx) { - volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; + volatile struct dmub_cursor_offload_v1 *cs = dmub->cursor_offload_v1; volatile struct dmub_shared_state_cursor_offload_stream_v1 *shared_stream; - const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); - uint32_t stream_idx, write_idx, payload_idx; + uint32_t write_idx, payload_idx; - if (pipe->plane_res.hubp) - pipe->plane_res.hubp->cursor_offload = false; + if (hubp) + hubp->cursor_offload = false; - if (pipe->plane_res.dpp) - pipe->plane_res.dpp->cursor_offload = false; + if (dpp) + dpp->cursor_offload = false; - if (!top_pipe) - return; - - stream_idx = top_pipe->pipe_idx; write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); - shared_stream = &dc->ctx->dmub_srv->dmub->shared_state[DMUB_SHARED_STATE_FEATURE__CURSOR_OFFLOAD_V1] + shared_stream = &dmub->shared_state[DMUB_SHARED_STATE_FEATURE__CURSOR_OFFLOAD_V1] .data.cursor_offload_v1.offload_streams[stream_idx]; shared_stream->last_write_idx = write_idx; @@ -1722,23 +1711,17 @@ void dcn35_commit_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pi cs->offload_streams[stream_idx].payloads[payload_idx].write_idx_finish = write_idx; } -void dcn35_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe) +void dcn35_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp) { - volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; - const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); - const struct hubp *hubp = pipe->plane_res.hubp; - const struct dpp *dpp = pipe->plane_res.dpp; + volatile struct dmub_cursor_offload_v1 *cs = dmub->cursor_offload_v1; volatile struct dmub_cursor_offload_pipe_data_dcn30_v1 *p; - uint32_t stream_idx, write_idx, payload_idx; + uint32_t write_idx, payload_idx; - if (!top_pipe || !hubp || !dpp) - return; - - stream_idx = top_pipe->pipe_idx; write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); - p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn30; + p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe_idx].dcn30; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH; @@ -1767,7 +1750,7 @@ void dcn35_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET = hubp->att.settings.bits.dst_y_offset; p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust; - cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx); + cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe_idx); } void dcn35_notify_cursor_offload_drr_update(struct dc *dc, struct dc_state *context, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h index 235ebf00bd1f..fce9acb5c2fc 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h @@ -103,10 +103,12 @@ bool dcn35_is_dp_dig_pixel_rate_div_policy(struct pipe_ctx *pipe_ctx); void dcn35_hardware_release(struct dc *dc); -void dcn35_abort_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe); -void dcn35_begin_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe); -void dcn35_commit_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe); -void dcn35_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe); +void dcn35_abort_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, struct hubp *hubp, uint32_t stream_idx); +void dcn35_begin_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, struct hubp *hubp, uint32_t stream_idx); +void dcn35_commit_cursor_offload_update(struct dmub_srv *dmub, struct dpp *dpp, + struct hubp *hubp, uint32_t stream_idx); +void dcn35_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp); void dcn35_notify_cursor_offload_drr_update(struct dc *dc, struct dc_state *context, const struct dc_stream_state *stream); void dcn35_program_cursor_offload_now(struct dc *dc, const struct pipe_ctx *pipe); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 7b089ae5c9c3..3a5dde5c8120 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -3089,23 +3089,17 @@ void dcn401_plane_atomic_power_down(struct dc *dc, hws->funcs.dpp_root_clock_control(hws, dpp->inst, false); } -void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe) +void dcn401_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp) { - volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; - const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); - const struct hubp *hubp = pipe->plane_res.hubp; - const struct dpp *dpp = pipe->plane_res.dpp; + volatile struct dmub_cursor_offload_v1 *cs = dmub->cursor_offload_v1; volatile struct dmub_cursor_offload_pipe_data_dcn401_v1 *p; - uint32_t stream_idx, write_idx, payload_idx; + uint32_t write_idx, payload_idx; - if (!top_pipe || !hubp || !dpp) - return; - - stream_idx = top_pipe->pipe_idx; write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); - p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn401; + p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe_idx].dcn401; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH; @@ -3142,7 +3136,7 @@ void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pip p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust; p->HUBP0_DCHUBP_MALL_CONFIG__USE_MALL_FOR_CURSOR = hubp->use_mall_for_cursor; - cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx); + cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe_idx); } void dcn401_plane_atomic_power_down_sequence(struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h index 756b2d98a930..81e3700d728b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h @@ -136,7 +136,8 @@ void dcn401_blank_pixel_data_sequence( bool blank, struct block_sequence_state *seq_state); void dcn401_initialize_min_clocks(struct dc *dc); -void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe); +void dcn401_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp); void dcn401_program_all_writeback_pipes_in_tree_sequence( struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c index c13d1ad8d000..8b63e75e5ee2 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c @@ -201,8 +201,14 @@ void dcn50_update_dchubp_dpp( if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || pipe_ctx->update_flags.bits.scaler || viewport_changed == true) && pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { - if (dc->hwss.abort_cursor_offload_update) - dc->hwss.abort_cursor_offload_update(dc, pipe_ctx); + if (dc_dmub_srv_is_cursor_offload_enabled(dc) && dc->hwss.abort_cursor_offload_update) { + struct pipe_ctx *top_pipe = resource_get_otg_master(pipe_ctx); + + if (top_pipe) + dc->hwss.abort_cursor_offload_update(dc->ctx->dmub_srv->dmub, + pipe_ctx->plane_res.dpp, pipe_ctx->plane_res.hubp, + top_pipe->pipe_idx); + } dc->hwss.set_cursor_attribute(pipe_ctx); dc->hwss.set_cursor_position(pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c index 0e6526e92544..5936f389d936 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c @@ -790,23 +790,17 @@ void dcn60_set_cursor_attribute(struct pipe_ctx *pipe_ctx) pipe_ctx->plane_res.dpp, attributes); } -void dcn60_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe) +void dcn60_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp) { - volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1; - const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe); - const struct hubp *hubp = pipe->plane_res.hubp; - const struct dpp *dpp = pipe->plane_res.dpp; + volatile struct dmub_cursor_offload_v1 *cs = dmub->cursor_offload_v1; volatile struct dmub_cursor_offload_pipe_data_dcn60_v1 *p; - uint32_t stream_idx, write_idx, payload_idx; + uint32_t write_idx, payload_idx; - if (!top_pipe || !hubp || !dpp) - return; - - stream_idx = top_pipe->pipe_idx; write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads); - p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn60; + p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe_idx].dcn60; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR; p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH; @@ -843,7 +837,7 @@ void dcn60_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust; p->HUBPREQ0_CURSOR_SETTINGS__FORCE_CURSOR_TO_DISP_PREF = hubp->att.settings.bits.force_cursor_to_disp_pref; - cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx); + cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe_idx); } /** diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h index b57d22d310b0..3a34150096df 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h @@ -24,7 +24,8 @@ enum dc_status dcn60_apply_single_controller_ctx_to_hw( void dcn60_init_hw(struct dc *dc); void dcn60_set_cursor_attribute(struct pipe_ctx *pipe_ctx); -void dcn60_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe); +void dcn60_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx, + uint8_t pipe_idx, const struct dpp *dpp, const struct hubp *hubp); void dcn60_program_perfmon(struct dc *dc, struct dc_state *context); bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index f75e34a09dca..0133c6156897 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -837,11 +837,6 @@ struct hubp_program_mcache_id_and_split_coordinate_params { struct mcache_regs_struct *mcache_regs; }; -struct abort_cursor_offload_update_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; -}; - struct cursor_lock_params { struct dc *dc; struct pipe_ctx *pipe_ctx; @@ -906,18 +901,32 @@ struct phantom_hubp_post_enable_params { }; struct begin_cursor_offload_update_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; + struct dmub_srv *dmub; + struct dpp *dpp; + struct hubp *hubp; + uint32_t stream_idx; }; struct update_cursor_offload_pipe_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; + struct dmub_srv *dmub; + const struct dpp *dpp; + const struct hubp *hubp; + uint32_t stream_idx; + uint8_t pipe_idx; }; struct commit_cursor_offload_update_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; + struct dmub_srv *dmub; + struct dpp *dpp; + struct hubp *hubp; + uint32_t stream_idx; +}; + +struct abort_cursor_offload_update_params { + struct dmub_srv *dmub; + struct dpp *dpp; + struct hubp *hubp; + uint32_t stream_idx; }; struct stream_enc_update_hdmi_info_packets_params { @@ -1535,10 +1544,17 @@ struct hw_sequencer_funcs { void (*set_cursor_position)(struct pipe_ctx *pipe); void (*set_cursor_attribute)(struct pipe_ctx *pipe); void (*set_cursor_sdr_white_level)(struct pipe_ctx *pipe); - void (*abort_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe); - void (*begin_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe); - void (*commit_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe); - void (*update_cursor_offload_pipe)(struct dc *dc, const struct pipe_ctx *pipe); + void (*abort_cursor_offload_update)(struct dmub_srv *dmub, struct dpp *dpp, + struct hubp *hubp, uint32_t stream_idx); + void (*begin_cursor_offload_update)(struct dmub_srv *dmub, struct dpp *dpp, + struct hubp *hubp, uint32_t stream_idx); + void (*commit_cursor_offload_update)(struct dmub_srv *dmub, struct dpp *dpp, + struct hubp *hubp, uint32_t stream_idx); + void (*update_cursor_offload_pipe)(struct dmub_srv *dmub, + uint32_t stream_idx, + uint8_t pipe_idx, + const struct dpp *dpp, + const struct hubp *hubp); void (*notify_cursor_offload_drr_update)(struct dc *dc, struct dc_state *context, const struct dc_stream_state *stream); void (*program_cursor_offload_now)(struct dc *dc, const struct pipe_ctx *pipe); @@ -2126,8 +2142,6 @@ void hwss_dpp_set_scaler(union block_sequence_params *params); void hwss_hubp_mem_program_viewport(union block_sequence_params *params); -void hwss_abort_cursor_offload_update(union block_sequence_params *params); - void hwss_send_cursor_info_to_dmu(union block_sequence_params *params); void hwss_set_cursor_attribute(union block_sequence_params *params); @@ -2152,11 +2166,13 @@ void hwss_phantom_hubp_post_enable(union block_sequence_params *params); void hwss_cursor_lock(union block_sequence_params *params); -void hwss_begin_cursor_offload_update(union block_sequence_params *params); +void hwss_begin_cursor_offload_update(struct dc *dc, union block_sequence_params *params); -void hwss_commit_cursor_offload_update(union block_sequence_params *params); +void hwss_commit_cursor_offload_update(struct dc *dc, union block_sequence_params *params); -void hwss_update_cursor_offload_pipe(union block_sequence_params *params); +void hwss_update_cursor_offload_pipe(struct dc *dc, union block_sequence_params *params); + +void hwss_abort_cursor_offload_update(struct dc *dc, union block_sequence_params *params); void hwss_setup_periodic_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx); From 54ec0f6414b36944c2b57e82a8d3a0359d1aed34 Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Tue, 25 Aug 2026 17:41:41 -0400 Subject: [PATCH 003/114] drm/amd/display: Update LLS and UPSP programming paths [Description] - UPSP should only be enabled when we want linear scaling for 420/422 - In DC for non-FP16 formats that are told to scale in non-linear, program bypass for PRE_GAM since these formats are already non-linear (i.e., YUV420, YUV422, RGB, etc.) Reviewed-by: Aric Cyr Reviewed-by: Taimur Hassan Signed-off-by: Alvin Lee Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/core/dc_resource.c | 18 ++++++++++++---- .../drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c | 20 +++++++++++++----- .../drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h | 3 ++- .../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 21 ++++++++++++++++++- drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h | 3 ++- drivers/gpu/drm/amd/display/dc/inc/resource.h | 3 ++- 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index f9b8e9474b4c..593c1fd537ae 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1557,11 +1557,21 @@ void resource_build_test_pattern_params(struct resource_context *res_ctx, } } -enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format) +enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format, + enum dc_scaling_linearity scaling_linearity) { - if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN && format <= SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb) //420 Formats + bool scaling_in_linear = (scaling_linearity == DC_SCALING_LINEARITY_LINEAR); + bool is_420_format = (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN && + format <= SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb); + bool is_422_format = (format > SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb && + format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END); + + /* UPSP (chroma upsampling) is only needed when subsampled YUV is scaled in + * linear space. + */ + if (scaling_in_linear && is_420_format) return UPSP_HORIZONTAL_VERTICAL_UPSAMPLING; - if (format > SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb && format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END) //422 Formats + if (scaling_in_linear && is_422_format) return UPSP_HORIZONTAL_UPSAMPLING_ONLY; return UPSP_BYPASS; } @@ -1611,7 +1621,7 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP; pipe_ctx->plane_res.scl_data.lb_params.alpha_en = plane_state->per_pixel_alpha; - pipe_ctx->plane_res.scl_data.upsp = resource_is_upsp_required(plane_state->format); + pipe_ctx->plane_res.scl_data.upsp = resource_is_upsp_required(plane_state->format, plane_state->scaling_linearity); // Convert pipe_ctx to respective input params for SPL translate_SPL_in_params_from_pipe_ctx(pipe_ctx, spl_in); diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c index 04cab7d3a5c2..38739ed048fa 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.c @@ -25,17 +25,27 @@ void dpp50_set_pregam_state( struct dpp *dpp_base, enum dc_transfer_func_predefined tr, - enum dc_scaling_linearity scaling) + enum dc_scaling_linearity scaling, + bool source_is_linear) { struct dcn50_dpp *dpp = TO_DCN50_DPP(dpp_base); enum pregam_mode pre_degam_en = PREGAM_DEGAM; enum degam_lut degamma_lut_selection = 0; if (scaling == DC_SCALING_LINEARITY_SOURCE) { - //If scaling in non-linear, apply regamma - REG_SET_2(PRE_GAM, 0, - PRE_GAM_MODE, PREGAM_REGAM, - PRE_REGAM_SELECT, REGAM_20); + if (source_is_linear) { + //Linear source data (e.g. FP16) must be de-linearized + //(apply regamma) before scaling in source/non-linear space. + REG_SET_2(PRE_GAM, 0, + PRE_GAM_MODE, PREGAM_REGAM, + PRE_REGAM_SELECT, REGAM_20); + } else { + //Non-linear source data (e.g. YUV) is already in source + //space, so scale directly with no pregam conversion. + REG_SET_2(PRE_GAM, 0, + PRE_GAM_MODE, PREGAM_BYPASS, + PRE_DEGAM_SELECT, 0); + } } else { //If scaling in linear, apply degamma based on TF switch (tr) { diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h index 7f5d3fa6bbef..c81e910ae095 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn50/dcn50_dpp.h @@ -82,6 +82,7 @@ void dpp50_dpp_setup( void dpp50_set_pregam_state( struct dpp *dpp_base, enum dc_transfer_func_predefined tr, - enum dc_scaling_linearity scaling); + enum dc_scaling_linearity scaling, + bool source_is_linear); #endif /* __DCN50_DPP_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index 79be5c385280..430966af3808 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -528,6 +528,24 @@ bool dcn32_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, return result; } +/* + * FP16 / 64bpp 16161616 surfaces store pixel data in linear light. These are + * the only source formats that must be de-linearized before scaling in + * source/non-linear space; every other format is already non-linear. + */ +static bool is_source_pixel_format_linear(enum surface_pixel_format format) +{ + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + return true; + default: + return false; + } +} + bool dcn32_set_input_transfer_func(struct set_input_transfer_func_params *params) { struct dce_hwseq *hws = params->dc->hwseq; @@ -547,7 +565,8 @@ bool dcn32_set_input_transfer_func(struct set_input_transfer_func_params *params tf = plane_state->in_transfer_func.tf; if (dpp->funcs->dpp_set_pregam_state) - dpp->funcs->dpp_set_pregam_state(dpp, tf, plane_state->scaling_linearity); + dpp->funcs->dpp_set_pregam_state(dpp, tf, plane_state->scaling_linearity, + is_source_pixel_format_linear(plane_state->format)); else dpp->funcs->dpp_set_pre_degam(dpp, tf); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h index b31b144d2e8f..930b5533256f 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h @@ -378,7 +378,8 @@ struct dpp_funcs { void (*dpp_set_pregam_state)( struct dpp *dpp_base, enum dc_transfer_func_predefined tr, - enum dc_scaling_linearity scaling); + enum dc_scaling_linearity scaling, + bool source_is_linear); void (*dpp_program_upsp)( struct dpp *dpp_base, const struct dscl_prog_data *dscl_prog_data); diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 8e21aaac06b3..9c6794f8dec0 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -123,7 +123,8 @@ void resource_build_test_pattern_params( struct resource_context *res_ctx, struct pipe_ctx *pipe_ctx); -enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format); +enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format, + enum dc_scaling_linearity scaling_linearity); bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx); From a22404942026fe124e1fcb1b6b59f6eebf512949 Mon Sep 17 00:00:00 2001 From: Horatio Zhang Date: Mon, 7 Sep 2026 21:43:13 +0800 Subject: [PATCH 004/114] drm/amdgpu: retire the legacy MES design for GFX 12.1 Retire the legacy design, always enable unified MES on GFX 12.1 and drop the driver code and MODULE_FIRMWARE entries that only served the legacy path. Signed-off-by: Horatio Zhang Acked-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 3 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 11 +- drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c | 475 +----------------- drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c | 9 +- drivers/gpu/drm/amd/amdgpu/mes_v12_1.c | 246 ++------- 5 files changed, 57 insertions(+), 687 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 4fd90e478bd2..ad21986d7b1b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -3281,8 +3281,7 @@ static int amdgpu_discovery_set_mes_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &mes_v12_1_ip_block); adev->enable_mes = true; adev->enable_mes_kiq = true; - if (amdgpu_uni_mes) - adev->enable_uni_mes = true; + adev->enable_uni_mes = true; break; default: break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index a6f95ff47d24..7a132e6d7e42 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1235,7 +1235,8 @@ uint32_t amdgpu_kiq_rreg(struct amdgpu_device *adev, uint32_t reg, uint32_t xcc_ if (adev->mes.ring[0].sched.ready) return amdgpu_mes_rreg(adev, reg, xcc_id); - BUG_ON(!ring->funcs->emit_rreg); + if (!ring->funcs || !ring->funcs->emit_rreg) + goto failed_kiq_read; spin_lock_irqsave(&kiq->ring_lock, flags); if (amdgpu_wb_get(adev, ®_val_offs)) { @@ -1303,8 +1304,6 @@ void amdgpu_kiq_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, uint3 struct amdgpu_kiq *kiq = &adev->gfx.kiq[xcc_id]; struct amdgpu_ring *ring = &kiq->ring; - BUG_ON(!ring->funcs->emit_wreg); - if (amdgpu_device_skip_hw_access(adev)) return; @@ -1313,6 +1312,9 @@ void amdgpu_kiq_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, uint3 return; } + if (!ring->funcs || !ring->funcs->emit_wreg) + goto failed_kiq_write; + spin_lock_irqsave(&kiq->ring_lock, flags); r = amdgpu_ring_alloc(ring, 32); if (r) @@ -1409,9 +1411,8 @@ int amdgpu_kiq_hdp_flush(struct amdgpu_device *adev) if (adev->enable_mes_kiq && adev->mes.ring[0].sched.ready) return amdgpu_mes_hdp_flush(adev); - if (!ring->funcs->emit_hdp_flush) { + if (!ring->funcs || !ring->funcs->emit_hdp_flush) return -EOPNOTSUPP; - } spin_lock_irqsave(&kiq->ring_lock, flags); r = amdgpu_ring_alloc(ring, 32); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index f812d6d81e9c..d7effa43c0fa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -203,12 +203,7 @@ static int gfx_v12_1_get_cu_info(struct amdgpu_device *adev, static uint64_t gfx_v12_1_get_gpu_clock_counter(struct amdgpu_device *adev); static void gfx_v12_1_xcc_select_se_sh(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 instance, int xcc_id); -static void gfx_v12_1_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, - uint32_t val); static int gfx_v12_1_wait_for_rlc_autoload_complete(struct amdgpu_device *adev); -static void gfx_v12_1_ring_invalidate_tlbs(struct amdgpu_ring *ring, - uint16_t pasid, uint32_t flush_type, - bool all_hub, uint8_t dst_sel); static void gfx_v12_1_xcc_set_safe_mode(struct amdgpu_device *adev, int xcc_id); static void gfx_v12_1_xcc_unset_safe_mode(struct amdgpu_device *adev, int xcc_id); static void gfx_v12_1_update_perf_clk(struct amdgpu_device *adev, @@ -219,142 +214,6 @@ static int gfx_v12_1_init_cp_compute_microcode_bo(struct amdgpu_device *adev); static void gfx_v12_1_xcc_update_medium_grain_clock_gating( struct amdgpu_device *adev, bool enable, int xcc_id, bool force); -static void gfx_v12_1_kiq_set_resources(struct amdgpu_ring *kiq_ring, - uint64_t queue_mask) -{ - amdgpu_ring_write(kiq_ring, PACKET3(PACKET3_SET_RESOURCES, 6)); - amdgpu_ring_write(kiq_ring, PACKET3_SET_RESOURCES_VMID_MASK(0) | - PACKET3_SET_RESOURCES_QUEUE_TYPE(0)); /* vmid_mask:0 queue_type:0 (KIQ) */ - amdgpu_ring_write(kiq_ring, lower_32_bits(queue_mask)); /* queue mask lo */ - amdgpu_ring_write(kiq_ring, upper_32_bits(queue_mask)); /* queue mask hi */ - amdgpu_ring_write(kiq_ring, 0); /* gws mask lo */ - amdgpu_ring_write(kiq_ring, 0); /* gws mask hi */ - amdgpu_ring_write(kiq_ring, 0); /* oac mask */ - amdgpu_ring_write(kiq_ring, 0); -} - -static void gfx_v12_1_kiq_map_queues(struct amdgpu_ring *kiq_ring, - struct amdgpu_ring *ring) -{ - uint64_t mqd_addr = amdgpu_bo_gpu_offset(ring->mqd_obj); - uint64_t wptr_addr = ring->wptr_gpu_addr; - uint32_t me = 0, eng_sel = 0; - - switch (ring->funcs->type) { - case AMDGPU_RING_TYPE_COMPUTE: - me = 1; - eng_sel = 0; - break; - case AMDGPU_RING_TYPE_MES: - me = 2; - eng_sel = 5; - break; - default: - WARN_ON(1); - } - - amdgpu_ring_write(kiq_ring, PACKET3(PACKET3_MAP_QUEUES, 5)); - /* Q_sel:0, vmid:0, vidmem: 1, engine:0, num_Q:1*/ - amdgpu_ring_write(kiq_ring, /* Q_sel: 0, vmid: 0, engine: 0, num_Q: 1 */ - PACKET3_MAP_QUEUES_QUEUE_SEL(0) | /* Queue_Sel */ - PACKET3_MAP_QUEUES_VMID(0) | /* VMID */ - PACKET3_MAP_QUEUES_QUEUE(ring->queue) | - PACKET3_MAP_QUEUES_PIPE(ring->pipe) | - PACKET3_MAP_QUEUES_ME((me)) | - PACKET3_MAP_QUEUES_QUEUE_TYPE(0) | /*queue_type: normal compute queue */ - PACKET3_MAP_QUEUES_ENGINE_SEL(eng_sel) | - PACKET3_MAP_QUEUES_NUM_QUEUES(1)); /* num_queues: must be 1 */ - amdgpu_ring_write(kiq_ring, PACKET3_MAP_QUEUES_DOORBELL_OFFSET(ring->doorbell_index)); - amdgpu_ring_write(kiq_ring, lower_32_bits(mqd_addr)); - amdgpu_ring_write(kiq_ring, upper_32_bits(mqd_addr)); - amdgpu_ring_write(kiq_ring, lower_32_bits(wptr_addr)); - amdgpu_ring_write(kiq_ring, upper_32_bits(wptr_addr)); -} - -static void gfx_v12_1_kiq_unmap_queues(struct amdgpu_ring *kiq_ring, - struct amdgpu_ring *ring, - enum amdgpu_unmap_queues_action action, - u64 gpu_addr, u64 seq) -{ - struct amdgpu_device *adev = kiq_ring->adev; - uint32_t eng_sel = ring->funcs->type == AMDGPU_RING_TYPE_GFX ? 4 : 0; - - if (adev->enable_mes && !adev->gfx.kiq[0].ring.sched.ready) { - amdgpu_mes_unmap_legacy_queue(adev, ring, action, gpu_addr, - seq, kiq_ring->xcc_id); - return; - } - - amdgpu_ring_write(kiq_ring, PACKET3(PACKET3_UNMAP_QUEUES, 4)); - amdgpu_ring_write(kiq_ring, /* Q_sel: 0, vmid: 0, engine: 0, num_Q: 1 */ - PACKET3_UNMAP_QUEUES_ACTION(action) | - PACKET3_UNMAP_QUEUES_QUEUE_SEL(0) | - PACKET3_UNMAP_QUEUES_ENGINE_SEL(eng_sel) | - PACKET3_UNMAP_QUEUES_NUM_QUEUES(1)); - amdgpu_ring_write(kiq_ring, - PACKET3_UNMAP_QUEUES_DOORBELL_OFFSET0(ring->doorbell_index)); - - if (action == PREEMPT_QUEUES_NO_UNMAP) { - amdgpu_ring_write(kiq_ring, lower_32_bits(gpu_addr)); - amdgpu_ring_write(kiq_ring, upper_32_bits(gpu_addr)); - amdgpu_ring_write(kiq_ring, seq); - } else { - amdgpu_ring_write(kiq_ring, 0); - amdgpu_ring_write(kiq_ring, 0); - amdgpu_ring_write(kiq_ring, 0); - } -} - -static void gfx_v12_1_kiq_query_status(struct amdgpu_ring *kiq_ring, - struct amdgpu_ring *ring, - u64 addr, u64 seq) -{ - uint32_t eng_sel = ring->funcs->type == AMDGPU_RING_TYPE_GFX ? 4 : 0; - - amdgpu_ring_write(kiq_ring, PACKET3(PACKET3_QUERY_STATUS, 5)); - amdgpu_ring_write(kiq_ring, - PACKET3_QUERY_STATUS_CONTEXT_ID(0) | - PACKET3_QUERY_STATUS_INTERRUPT_SEL(0) | - PACKET3_QUERY_STATUS_COMMAND(2)); - amdgpu_ring_write(kiq_ring, /* Q_sel: 0, vmid: 0, engine: 0, num_Q: 1 */ - PACKET3_QUERY_STATUS_DOORBELL_OFFSET(ring->doorbell_index) | - PACKET3_QUERY_STATUS_ENG_SEL(eng_sel)); - amdgpu_ring_write(kiq_ring, lower_32_bits(addr)); - amdgpu_ring_write(kiq_ring, upper_32_bits(addr)); - amdgpu_ring_write(kiq_ring, lower_32_bits(seq)); - amdgpu_ring_write(kiq_ring, upper_32_bits(seq)); -} - -static void gfx_v12_1_kiq_invalidate_tlbs(struct amdgpu_ring *kiq_ring, - uint16_t pasid, - uint32_t flush_type, - bool all_hub) -{ - gfx_v12_1_ring_invalidate_tlbs(kiq_ring, pasid, flush_type, all_hub, 1); -} - -static const struct kiq_pm4_funcs gfx_v12_1_kiq_pm4_funcs = { - .kiq_set_resources = gfx_v12_1_kiq_set_resources, - .kiq_map_queues = gfx_v12_1_kiq_map_queues, - .kiq_unmap_queues = gfx_v12_1_kiq_unmap_queues, - .kiq_query_status = gfx_v12_1_kiq_query_status, - .kiq_invalidate_tlbs = gfx_v12_1_kiq_invalidate_tlbs, - .set_resources_size = 8, - .map_queues_size = 7, - .unmap_queues_size = 6, - .query_status_size = 7, - .invalidate_tlbs_size = 2, -}; - -static void gfx_v12_1_set_kiq_pm4_funcs(struct amdgpu_device *adev) -{ - int i, num_xcc; - - num_xcc = NUM_XCC(adev->gfx.xcc_mask); - for (i =0; i < num_xcc; i++) - adev->gfx.kiq[i].pmf = &gfx_v12_1_kiq_pm4_funcs; -} - static void gfx_v12_1_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, int mem_space, int opt, uint32_t addr0, uint32_t addr1, uint32_t ref, @@ -403,14 +262,9 @@ static int gfx_v12_1_ring_test_ring(struct amdgpu_ring *ring) return r; } - if (ring->funcs->type == AMDGPU_RING_TYPE_KIQ) { - gfx_v12_1_ring_emit_wreg(ring, xcc_offset, 0xDEADBEEF); - } else { - amdgpu_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1)); - amdgpu_ring_write(ring, xcc_offset - - PACKET3_SET_UCONFIG_REG_START); - amdgpu_ring_write(ring, 0xDEADBEEF); - } + amdgpu_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1)); + amdgpu_ring_write(ring, xcc_offset - PACKET3_SET_UCONFIG_REG_START); + amdgpu_ring_write(ring, 0xDEADBEEF); amdgpu_ring_commit(ring); for (i = 0; i < adev->usec_timeout; i++) { @@ -439,11 +293,6 @@ static int gfx_v12_1_ring_test_ib(struct amdgpu_ring *ring, long timeout) volatile uint32_t *cpu_ptr; long r; - /* MES KIQ fw hasn't indirect buffer support for now */ - if (adev->enable_mes_kiq && - ring->funcs->type == AMDGPU_RING_TYPE_KIQ) - return 0; - r = amdgpu_wb_get(adev, &index); if (r) return r; @@ -1538,18 +1387,6 @@ static int gfx_v12_1_sw_init(struct amdgpu_ip_block *ip_block) } } - if (!adev->enable_mes_kiq) { - r = amdgpu_gfx_kiq_init(adev, GFX12_MEC_HPD_SIZE, xcc_id); - if (r) { - dev_err(adev->dev, "Failed to init KIQ BOs!\n"); - return r; - } - - r = amdgpu_gfx_kiq_init_ring(adev, xcc_id); - if (r) - return r; - } - r = amdgpu_gfx_mqd_sw_init(adev, sizeof(struct v12_1_compute_mqd), xcc_id); if (r) return r; @@ -1600,15 +1437,9 @@ static int gfx_v12_1_sw_fini(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->gfx.num_compute_rings * num_xcc; i++) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); - for (i = 0; i < num_xcc; i++) { + for (i = 0; i < num_xcc; i++) amdgpu_gfx_mqd_sw_fini(adev, i); - if (!adev->enable_mes_kiq) { - amdgpu_gfx_kiq_free_ring(&adev->gfx.kiq[i].ring); - amdgpu_gfx_kiq_fini(adev, i); - } - } - gfx_v12_1_rlc_fini(adev); gfx_v12_1_mec_fini(adev); @@ -2193,8 +2024,6 @@ static void gfx_v12_1_xcc_cp_compute_enable(struct amdgpu_device *adev, enable ? 0 : 1); WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MEC_RS64_CNTL, data); - adev->gfx.kiq[xcc_id].ring.sched.ready = enable; - udelay(50); } @@ -2357,21 +2186,6 @@ static int gfx_v12_1_xcc_cp_compute_load_microcode_rs64(struct amdgpu_device *ad return 0; } -static void gfx_v12_1_xcc_kiq_setting(struct amdgpu_ring *ring, - int xcc_id) -{ - uint32_t tmp; - struct amdgpu_device *adev = ring->adev; - - /* tell RLC which is KIQ queue */ - tmp = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CP_SCHEDULERS); - tmp &= 0xffffff00; - tmp |= (ring->me << 5) | (ring->pipe << 3) | (ring->queue); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CP_SCHEDULERS, tmp); - tmp |= 0x80; - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CP_SCHEDULERS, tmp); -} - static void gfx_v12_1_xcc_cp_set_doorbell_range(struct amdgpu_device *adev, int xcc_id) { @@ -2527,149 +2341,6 @@ static int gfx_v12_1_compute_mqd_init(struct amdgpu_device *adev, void *m, return 0; } -static int gfx_v12_1_xcc_kiq_init_register(struct amdgpu_ring *ring, - int xcc_id) -{ - struct amdgpu_device *adev = ring->adev; - struct v12_1_compute_mqd *mqd = ring->mqd_ptr; - int j; - - /* inactivate the queue */ - if (amdgpu_sriov_vf(adev)) - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE, 0); - - /* disable wptr polling */ - WREG32_FIELD15_PREREG(GC, GET_INST(GC, xcc_id), CP_PQ_WPTR_POLL_CNTL, EN, 0); - - /* write the EOP addr */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_EOP_BASE_ADDR, - mqd->cp_hqd_eop_base_addr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_EOP_BASE_ADDR_HI, - mqd->cp_hqd_eop_base_addr_hi); - - /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_EOP_CONTROL, - mqd->cp_hqd_eop_control); - - /* enable doorbell? */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_DOORBELL_CONTROL, - mqd->cp_hqd_pq_doorbell_control); - - /* disable the queue if it's active */ - if (RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE) & 1) { - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_DEQUEUE_REQUEST, 1); - for (j = 0; j < adev->usec_timeout; j++) { - if (!(RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE) & 1)) - break; - udelay(1); - } - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_DEQUEUE_REQUEST, - mqd->cp_hqd_dequeue_request); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_RPTR, - mqd->cp_hqd_pq_rptr); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_LO, - mqd->cp_hqd_pq_wptr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_HI, - mqd->cp_hqd_pq_wptr_hi); - } - - /* set the pointer to the MQD */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MQD_BASE_ADDR, - mqd->cp_mqd_base_addr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MQD_BASE_ADDR_HI, - mqd->cp_mqd_base_addr_hi); - - /* set MQD vmid to 0 */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MQD_CONTROL, - mqd->cp_mqd_control); - - /* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_BASE, - mqd->cp_hqd_pq_base_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_BASE_HI, - mqd->cp_hqd_pq_base_hi); - - /* set up the HQD, this is similar to CP_RB0_CNTL */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_CONTROL, - mqd->cp_hqd_pq_control); - - /* set the wb address whether it's enabled or not */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_RPTR_REPORT_ADDR, - mqd->cp_hqd_pq_rptr_report_addr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_RPTR_REPORT_ADDR_HI, - mqd->cp_hqd_pq_rptr_report_addr_hi); - - /* only used if CP_PQ_WPTR_POLL_CNTL.CP_PQ_WPTR_POLL_CNTL__EN_MASK=1 */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_POLL_ADDR, - mqd->cp_hqd_pq_wptr_poll_addr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_POLL_ADDR_HI, - mqd->cp_hqd_pq_wptr_poll_addr_hi); - - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_DOORBELL_CONTROL, - mqd->cp_hqd_pq_doorbell_control); - - /* reset read and write pointers, similar to CP_RB0_WPTR/_RPTR */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_LO, - mqd->cp_hqd_pq_wptr_lo); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_HI, - mqd->cp_hqd_pq_wptr_hi); - - /* set the vmid for the queue */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_VMID, mqd->cp_hqd_vmid); - - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PERSISTENT_STATE, - mqd->cp_hqd_persistent_state); - - /* activate the queue */ - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE, - mqd->cp_hqd_active); - - if (ring->use_doorbell) - WREG32_FIELD15_PREREG(GC, GET_INST(GC, xcc_id), CP_PQ_STATUS, DOORBELL_ENABLE, 1); - - return 0; -} - -static int gfx_v12_1_xcc_kiq_init_queue(struct amdgpu_ring *ring, - int xcc_id) -{ - struct amdgpu_device *adev = ring->adev; - struct v12_1_compute_mqd *mqd = ring->mqd_ptr; - - gfx_v12_1_xcc_kiq_setting(ring, xcc_id); - - if (amdgpu_in_reset(adev)) { /* for GPU_RESET case */ - /* reset MQD to a clean status */ - if (adev->gfx.kiq[xcc_id].mqd_backup) - memcpy(mqd, adev->gfx.kiq[xcc_id].mqd_backup, sizeof(*mqd)); - - /* reset ring buffer */ - ring->wptr = 0; - amdgpu_ring_clear_ring(ring); - - mutex_lock(&adev->srbm_mutex); - soc_v1_0_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, GET_INST(GC, xcc_id)); - gfx_v12_1_xcc_kiq_init_register(ring, xcc_id); - soc_v1_0_grbm_select(adev, 0, 0, 0, 0, GET_INST(GC, xcc_id)); - mutex_unlock(&adev->srbm_mutex); - } else { - memset((void *)mqd, 0, sizeof(*mqd)); - if (amdgpu_sriov_vf(adev) && adev->in_suspend) - amdgpu_ring_clear_ring(ring); - mutex_lock(&adev->srbm_mutex); - soc_v1_0_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, GET_INST(GC, xcc_id)); - amdgpu_ring_init_mqd(ring); - gfx_v12_1_xcc_kiq_init_register(ring, xcc_id); - soc_v1_0_grbm_select(adev, 0, 0, 0, 0, GET_INST(GC, xcc_id)); - mutex_unlock(&adev->srbm_mutex); - - if (adev->gfx.kiq[xcc_id].mqd_backup) - memcpy(adev->gfx.kiq[xcc_id].mqd_backup, mqd, sizeof(*mqd)); - } - - return 0; -} - static int gfx_v12_1_xcc_kcq_init_queue(struct amdgpu_ring *ring, int xcc_id) { @@ -2700,32 +2371,6 @@ static int gfx_v12_1_xcc_kcq_init_queue(struct amdgpu_ring *ring, return 0; } -static int gfx_v12_1_xcc_kiq_resume(struct amdgpu_device *adev, - int xcc_id) -{ - struct amdgpu_ring *ring; - int r; - - ring = &adev->gfx.kiq[xcc_id].ring; - - r = amdgpu_bo_reserve(ring->mqd_obj, false); - if (unlikely(r != 0)) - return r; - - r = amdgpu_bo_kmap(ring->mqd_obj, (void **)&ring->mqd_ptr); - if (unlikely(r != 0)) { - amdgpu_bo_unreserve(ring->mqd_obj); - return r; - } - - gfx_v12_1_xcc_kiq_init_queue(ring, xcc_id); - amdgpu_bo_kunmap(ring->mqd_obj); - ring->mqd_ptr = NULL; - amdgpu_bo_unreserve(ring->mqd_obj); - ring->sched.ready = true; - return 0; -} - static int gfx_v12_1_xcc_kcq_resume(struct amdgpu_device *adev, int xcc_id) { @@ -2762,6 +2407,9 @@ static int gfx_v12_1_xcc_cp_resume(struct amdgpu_device *adev, uint16_t xcc_mask int r, i, xcc_id; struct amdgpu_ring *ring; + if (!adev->mes.kiq_hw_init) + return -EINVAL; + for_each_inst(xcc_id, xcc_mask) { if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) { /* legacy firmware loading */ @@ -2779,19 +2427,7 @@ static int gfx_v12_1_xcc_cp_resume(struct amdgpu_device *adev, uint16_t xcc_mask gfx_v12_1_xcc_cp_compute_enable(adev, true, xcc_id); - if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) { - r = amdgpu_mes_kiq_hw_init(adev, xcc_id); - /* - * With MES, GFX KIQ ring is owned by the MES and is never - * initialized/used directly by the driver, so it must - * not be left flagged as ready. mes_v12_0_hw_init() clears - * but clear here if MES init fails - */ - if (r) - adev->gfx.kiq[xcc_id].ring.sched.ready = false; - } else { - r = gfx_v12_1_xcc_kiq_resume(adev, xcc_id); - } + r = amdgpu_mes_kiq_hw_init(adev, xcc_id); if (r) return r; @@ -3184,7 +2820,6 @@ static int gfx_v12_1_early_init(struct amdgpu_ip_block *ip_block) adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev), AMDGPU_MAX_COMPUTE_RINGS); - gfx_v12_1_set_kiq_pm4_funcs(adev); gfx_v12_1_set_ring_funcs(adev); gfx_v12_1_set_irq_funcs(adev); gfx_v12_1_set_rlc_funcs(adev); @@ -3685,18 +3320,6 @@ static void gfx_v12_1_ring_emit_pipeline_sync(struct amdgpu_ring *ring) upper_32_bits(addr), seq, 0xffffffff, 4); } -static void gfx_v12_1_ring_invalidate_tlbs(struct amdgpu_ring *ring, - uint16_t pasid, uint32_t flush_type, - bool all_hub, uint8_t dst_sel) -{ - amdgpu_ring_write(ring, PACKET3(PACKET3_INVALIDATE_TLBS, 0)); - amdgpu_ring_write(ring, - PACKET3_INVALIDATE_TLBS_DST_SEL(dst_sel) | - PACKET3_INVALIDATE_TLBS_ALL_HUB(all_hub) | - PACKET3_INVALIDATE_TLBS_PASID(pasid) | - PACKET3_INVALIDATE_TLBS_FLUSH_TYPE(flush_type)); -} - static void gfx_v12_1_ring_emit_vm_flush(struct amdgpu_ring *ring, unsigned vmid, uint64_t pd_addr) { @@ -3705,65 +3328,14 @@ static void gfx_v12_1_ring_emit_vm_flush(struct amdgpu_ring *ring, /* compute doesn't have PFP */ } -static void gfx_v12_1_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, - u64 seq, unsigned int flags) -{ - struct amdgpu_device *adev = ring->adev; - - /* write fence seq to the "addr" */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (PACKET3_WRITE_DATA__DST_SEL(5) | PACKET3_WRITE_DATA__WR_CONFIRM(1))); - amdgpu_ring_write(ring, lower_32_bits(addr)); - amdgpu_ring_write(ring, upper_32_bits(addr)); - amdgpu_ring_write(ring, lower_32_bits(seq)); - - if (flags & AMDGPU_FENCE_FLAG_INT) { - /* set register to trigger INT */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (PACKET3_WRITE_DATA__DST_SEL(0) | PACKET3_WRITE_DATA__WR_CONFIRM(1))); - amdgpu_ring_write(ring, SOC15_REG_OFFSET(GC, GET_INST(GC, 0), regCPC_INT_STATUS)); - amdgpu_ring_write(ring, 0); - amdgpu_ring_write(ring, 0x20000000); /* src_id is 178 */ - } -} - -static void gfx_v12_1_ring_emit_rreg(struct amdgpu_ring *ring, uint32_t reg, - uint32_t reg_val_offs) -{ - struct amdgpu_device *adev = ring->adev; - - reg = soc_v1_0_normalize_xcc_reg_offset(reg); - - amdgpu_ring_write(ring, PACKET3(PACKET3_COPY_DATA, 4)); - amdgpu_ring_write(ring, 0 | /* src: register*/ - (5 << 8) | /* dst: memory */ - (1 << 20)); /* write confirm */ - amdgpu_ring_write(ring, reg); - amdgpu_ring_write(ring, 0); - amdgpu_ring_write(ring, lower_32_bits(adev->wb.gpu_addr + - reg_val_offs * 4)); - amdgpu_ring_write(ring, upper_32_bits(adev->wb.gpu_addr + - reg_val_offs * 4)); -} - static void gfx_v12_1_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val) { - uint32_t cmd = 0; - reg = soc_v1_0_normalize_xcc_reg_offset(reg); - switch (ring->funcs->type) { - case AMDGPU_RING_TYPE_KIQ: - cmd = (1 << 16); /* no inc addr */ - break; - default: - cmd = PACKET3_WRITE_DATA__WR_CONFIRM(1); - break; - } amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, cmd); + amdgpu_ring_write(ring, PACKET3_WRITE_DATA__WR_CONFIRM(1)); amdgpu_ring_write(ring, reg); amdgpu_ring_write(ring, 0); amdgpu_ring_write(ring, val); @@ -4157,41 +3729,12 @@ static const struct amdgpu_ring_funcs gfx_v12_1_ring_funcs_compute = { .emit_mem_sync = gfx_v12_1_emit_mem_sync, }; -static const struct amdgpu_ring_funcs gfx_v12_1_ring_funcs_kiq = { - .type = AMDGPU_RING_TYPE_KIQ, - .align_mask = 0xff, - .nop = PACKET3(PACKET3_NOP, 0x3FFF), - .support_64bit_ptrs = true, - .get_rptr = gfx_v12_1_ring_get_rptr_compute, - .get_wptr = gfx_v12_1_ring_get_wptr_compute, - .set_wptr = gfx_v12_1_ring_set_wptr_compute, - .emit_frame_size = - 7 + /* gfx_v12_1_ring_emit_pipeline_sync */ - SOC15_FLUSH_GPU_TLB_NUM_WREG * 5 + - SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 7 + - 2 + /* gfx_v12_1_ring_emit_vm_flush */ - 8 + 8 + 8, /* gfx_v12_1_ring_emit_fence_kiq x3 for user fence, vm fence */ - .emit_ib_size = 7, /* gfx_v12_1_ring_emit_ib_compute */ - .emit_ib = gfx_v12_1_ring_emit_ib_compute, - .emit_fence = gfx_v12_1_ring_emit_fence_kiq, - .test_ring = gfx_v12_1_ring_test_ring, - .test_ib = gfx_v12_1_ring_test_ib, - .insert_nop = amdgpu_ring_insert_nop, - .pad_ib = amdgpu_ring_generic_pad_ib, - .emit_rreg = gfx_v12_1_ring_emit_rreg, - .emit_wreg = gfx_v12_1_ring_emit_wreg, - .emit_reg_wait = gfx_v12_1_ring_emit_reg_wait, - .emit_reg_write_reg_wait = gfx_v12_1_ring_emit_reg_write_reg_wait, -}; - static void gfx_v12_1_set_ring_funcs(struct amdgpu_device *adev) { int i, j, num_xcc; num_xcc = NUM_XCC(adev->gfx.xcc_mask); for (i = 0; i < num_xcc; i++) { - adev->gfx.kiq[i].ring.funcs = &gfx_v12_1_ring_funcs_kiq; - for (j = 0; j < adev->gfx.num_compute_rings; j++) adev->gfx.compute_ring[j + i * adev->gfx.num_compute_rings].funcs = &gfx_v12_1_ring_funcs_compute; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c index 565ee7fba3cd..3349e8bc1c20 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c @@ -407,9 +407,8 @@ static void gmc_v12_1_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, /* This is necessary for SRIOV as well as for GFXOFF to function * properly under bare metal */ - if (((adev->gfx.kiq[inst].ring.sched.ready || - adev->mes.ring[MES_PIPE_INST(inst, 0)].sched.ready) && - (amdgpu_sriov_runtime(adev) || !amdgpu_sriov_vf(adev)))) { + if (adev->mes.ring[MES_PIPE_INST(inst, 0)].sched.ready && + (amdgpu_sriov_runtime(adev) || !amdgpu_sriov_vf(adev))) { struct amdgpu_vmhub *hub = &adev->vmhub[vmhub]; const unsigned eng = 17; u32 inv_req = hub->vmhub_funcs->get_invalidate_req(vmid, flush_type); @@ -432,7 +431,7 @@ static void gmc_v12_1_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, * @pasid: pasid to be flush * @flush_type: the flush type * @all_hub: flush all hubs - * @inst: is used to select which instance of KIQ to use for the invalidation + * @inst: XCC instance to use for the invalidation * * Flush the TLB for the requested pasid. */ @@ -443,7 +442,7 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev, uint16_t queried; int vmid, i; - if (adev->enable_uni_mes && adev->mes.ring[0].sched.ready && + if (adev->mes.ring[0].sched.ready && (adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x6f) { struct mes_inv_tlbs_pasid_input input = {0}; input.xcc_id = inst; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index c827eb46058a..a69f9b9c5cd6 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -34,8 +34,6 @@ #include "gfx_v12_1_pkt.h" #include "sdma_v7_1_0_pkt_open.h" -MODULE_FIRMWARE("amdgpu/gc_12_1_0_mes.bin"); -MODULE_FIRMWARE("amdgpu/gc_12_1_0_mes1.bin"); MODULE_FIRMWARE("amdgpu/gc_12_1_0_uni_mes.bin"); static int mes_v12_1_hw_init(struct amdgpu_ip_block *ip_block); @@ -414,15 +412,11 @@ static int mes_v12_1_map_legacy_queue(struct amdgpu_mes *mes, convert_to_mes_queue_type(input->queue_type); mes_add_queue_pkt.map_legacy_kq = 1; - if (mes->adev->enable_uni_mes) { - /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ - if (input->queue_type == AMDGPU_RING_TYPE_MES) - pipe = AMDGPU_MES_KIQ_PIPE; - else - pipe = AMDGPU_MES_SCHED_PIPE; - } else { + /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else pipe = AMDGPU_MES_SCHED_PIPE; - } return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, pipe, @@ -459,15 +453,11 @@ static int mes_v12_1_unmap_legacy_queue(struct amdgpu_mes *mes, convert_to_mes_queue_type(input->queue_type); } - if (mes->adev->enable_uni_mes) { - /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ - if (input->queue_type == AMDGPU_RING_TYPE_MES) - pipe = AMDGPU_MES_KIQ_PIPE; - else - pipe = AMDGPU_MES_SCHED_PIPE; - } else { + /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else pipe = AMDGPU_MES_SCHED_PIPE; - } return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, pipe, @@ -567,7 +557,7 @@ static int mes_v12_1_misc_op(struct amdgpu_mes *mes, int pipe; /*OP_WRM_REG_WR_WAIT is used to do tlb invalidation which need to be handled in sched pipe for gfx_12_1.*/ - if (mes->adev->enable_uni_mes && input->op != MES_MISC_OP_WRM_REG_WR_WAIT) + if (input->op != MES_MISC_OP_WRM_REG_WR_WAIT) pipe = AMDGPU_MES_KIQ_PIPE; else pipe = AMDGPU_MES_SCHED_PIPE; @@ -766,7 +756,7 @@ static int mes_v12_1_set_hw_resources(struct amdgpu_mes *mes, if (pipe == AMDGPU_MES_SCHED_PIPE) adev->mes.sched_version = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MES_GP3_LO); - else if (pipe == AMDGPU_MES_KIQ_PIPE && adev->enable_mes_kiq) + else if (pipe == AMDGPU_MES_KIQ_PIPE) adev->mes.kiq_version = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MES_GP3_LO); soc_v1_0_grbm_select(adev, 0, 0, 0, 0, GET_INST(GC, xcc_id)); @@ -880,10 +870,7 @@ static int mes_v12_1_reset_legacy_queue(struct amdgpu_mes *mes, mes_reset_queue_pkt.doorbell_offset = input->doorbell_offset; } - if (mes->adev->enable_uni_mes) - pipe = AMDGPU_MES_KIQ_PIPE; - else - pipe = AMDGPU_MES_SCHED_PIPE; + pipe = AMDGPU_MES_KIQ_PIPE; return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, pipe, @@ -1100,10 +1087,8 @@ static void mes_v12_1_enable(struct amdgpu_device *adev, if (amdgpu_emu_mode) msleep(500); - else if (adev->enable_uni_mes) - udelay(500); else - udelay(50); + udelay(500); } else { data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_MES_CNTL); data = REG_SET_FIELD(data, CP_MES_CNTL, MES_PIPE0_ACTIVE, 0); @@ -1426,31 +1411,6 @@ static void mes_v12_1_queue_init_register(struct amdgpu_ring *ring, mutex_unlock(&adev->srbm_mutex); } -static int mes_v12_1_kiq_enable_queue(struct amdgpu_device *adev, int xcc_id) -{ - struct amdgpu_kiq *kiq = &adev->gfx.kiq[xcc_id]; - struct amdgpu_ring *kiq_ring = &adev->gfx.kiq[xcc_id].ring; - int r, inst = MES_PIPE_INST(xcc_id, AMDGPU_MES_SCHED_PIPE); - - if (!kiq->pmf || !kiq->pmf->kiq_map_queues) - return -EINVAL; - - r = amdgpu_ring_alloc(kiq_ring, kiq->pmf->map_queues_size); - if (r) { - DRM_ERROR("Failed to lock KIQ (%d).\n", r); - return r; - } - - kiq->pmf->kiq_map_queues(kiq_ring, &adev->mes.ring[inst]); - - r = amdgpu_ring_test_ring(kiq_ring); - if (r) { - DRM_ERROR("kfq enable failed\n"); - kiq_ring->sched.ready = false; - } - return r; -} - static int mes_v12_1_queue_init(struct amdgpu_device *adev, enum amdgpu_mes_pipe pipe, int xcc_id) @@ -1458,13 +1418,9 @@ static int mes_v12_1_queue_init(struct amdgpu_device *adev, struct amdgpu_ring *ring; int r; - if (!adev->enable_uni_mes && pipe == AMDGPU_MES_KIQ_PIPE) - ring = &adev->gfx.kiq[xcc_id].ring; - else - ring = &adev->mes.ring[MES_PIPE_INST(xcc_id, pipe)]; + ring = &adev->mes.ring[MES_PIPE_INST(xcc_id, pipe)]; - if ((adev->enable_uni_mes || pipe == AMDGPU_MES_SCHED_PIPE) && - (amdgpu_in_reset(adev) || adev->in_suspend)) { + if (amdgpu_in_reset(adev) || adev->in_suspend) { *(ring->wptr_cpu_addr) = 0; *(ring->rptr_cpu_addr) = 0; amdgpu_ring_clear_ring(ring); @@ -1475,10 +1431,7 @@ static int mes_v12_1_queue_init(struct amdgpu_device *adev, return r; if (pipe == AMDGPU_MES_SCHED_PIPE) { - if (adev->enable_uni_mes) - r = amdgpu_mes_map_legacy_queue(adev, ring, xcc_id); - else - r = mes_v12_1_kiq_enable_queue(adev, xcc_id); + r = amdgpu_mes_map_legacy_queue(adev, ring, xcc_id); if (r) return r; } else { @@ -1527,39 +1480,6 @@ static int mes_v12_1_ring_init(struct amdgpu_device *adev, AMDGPU_RING_PRIO_DEFAULT, NULL); } -static int mes_v12_1_kiq_ring_init(struct amdgpu_device *adev, int xcc_id) -{ - struct amdgpu_ring *ring; - int inst = MES_PIPE_INST(xcc_id, AMDGPU_MES_KIQ_PIPE); - - spin_lock_init(&adev->gfx.kiq[xcc_id].ring_lock); - - ring = &adev->gfx.kiq[xcc_id].ring; - - ring->me = 3; - ring->pipe = 1; - ring->queue = 0; - ring->xcc_id = xcc_id; - ring->vm_hub = AMDGPU_GFXHUB(xcc_id); - - ring->adev = NULL; - ring->ring_obj = NULL; - ring->use_doorbell = true; - ring->eop_gpu_addr = adev->mes.eop_gpu_addr[inst]; - ring->no_scheduler = true; - ring->doorbell_index = - (adev->doorbell_index.mes_ring1 + - xcc_id * adev->doorbell_index.xcc_doorbell_range) - << 1; - - snprintf(ring->name, sizeof(ring->name), "mes_kiq_%hhu.%hhu.%hhu.%hhu", - (unsigned char)xcc_id, (unsigned char)ring->me, - (unsigned char)ring->pipe, (unsigned char)ring->queue); - - return amdgpu_ring_init(adev, ring, 1024, NULL, 0, - AMDGPU_RING_PRIO_DEFAULT, NULL); -} - static int mes_v12_1_mqd_sw_init(struct amdgpu_device *adev, enum amdgpu_mes_pipe pipe, int xcc_id) @@ -1568,10 +1488,7 @@ static int mes_v12_1_mqd_sw_init(struct amdgpu_device *adev, struct amdgpu_ring *ring; int inst = MES_PIPE_INST(xcc_id, pipe); - if (!adev->enable_uni_mes && pipe == AMDGPU_MES_KIQ_PIPE) - ring = &adev->gfx.kiq[xcc_id].ring; - else - ring = &adev->mes.ring[inst]; + ring = &adev->mes.ring[inst]; if (ring->mqd_obj) return 0; @@ -1606,8 +1523,8 @@ static int mes_v12_1_sw_init(struct amdgpu_ip_block *ip_block) adev->mes.kiq_hw_fini = &mes_v12_1_kiq_hw_fini; adev->mes.enable_legacy_queue_map = true; - adev->mes.event_log_size = - adev->enable_uni_mes ? (AMDGPU_MAX_MES_PIPES * AMDGPU_MES_LOG_BUFFER_SIZE * num_xcc) : AMDGPU_MES_LOG_BUFFER_SIZE; + adev->mes.event_log_size = AMDGPU_MAX_MES_PIPES * + AMDGPU_MES_LOG_BUFFER_SIZE * num_xcc; r = amdgpu_mes_init(adev); if (r) @@ -1623,15 +1540,11 @@ static int mes_v12_1_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - if (!adev->enable_uni_mes && pipe == - AMDGPU_MES_KIQ_PIPE) - r = mes_v12_1_kiq_ring_init(adev, xcc_id); - else - r = mes_v12_1_ring_init(adev, xcc_id, pipe); + r = mes_v12_1_ring_init(adev, xcc_id, pipe); if (r) return r; - if (adev->enable_uni_mes && num_xcc > 1) { + if (num_xcc > 1) { r = mes_v12_1_allocate_shared_cmd_buf(adev, pipe, xcc_id); if (r) @@ -1662,12 +1575,10 @@ static int mes_v12_1_sw_fini(struct amdgpu_ip_block *ip_block) &adev->mes.eop_gpu_addr[inst], NULL); - if (adev->enable_uni_mes || pipe == AMDGPU_MES_SCHED_PIPE) { - amdgpu_bo_free_kernel(&adev->mes.ring[inst].mqd_obj, - &adev->mes.ring[inst].mqd_gpu_addr, - &adev->mes.ring[inst].mqd_ptr); - amdgpu_ring_fini(&adev->mes.ring[inst]); - } + amdgpu_bo_free_kernel(&adev->mes.ring[inst].mqd_obj, + &adev->mes.ring[inst].mqd_gpu_addr, + &adev->mes.ring[inst].mqd_ptr); + amdgpu_ring_fini(&adev->mes.ring[inst]); } } @@ -1675,13 +1586,6 @@ static int mes_v12_1_sw_fini(struct amdgpu_ip_block *ip_block) amdgpu_ucode_release(&adev->mes.fw[pipe]); for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) { - if (!adev->enable_uni_mes) { - amdgpu_bo_free_kernel(&adev->gfx.kiq[xcc_id].ring.mqd_obj, - &adev->gfx.kiq[xcc_id].ring.mqd_gpu_addr, - &adev->gfx.kiq[xcc_id].ring.mqd_ptr); - amdgpu_ring_fini(&adev->gfx.kiq[xcc_id].ring); - } - if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) { mes_v12_1_free_ucode_buffers(adev, AMDGPU_MES_KIQ_PIPE, xcc_id); @@ -1694,44 +1598,6 @@ static int mes_v12_1_sw_fini(struct amdgpu_ip_block *ip_block) return 0; } -static void mes_v12_1_kiq_dequeue_sched(struct amdgpu_device *adev, - int xcc_id) -{ - uint32_t data; - int i; - - mutex_lock(&adev->srbm_mutex); - soc_v1_0_grbm_select(adev, 3, AMDGPU_MES_SCHED_PIPE, 0, 0, - GET_INST(GC, xcc_id)); - - /* disable the queue if it's active */ - if (RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE) & 1) { - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_DEQUEUE_REQUEST, 1); - for (i = 0; i < adev->usec_timeout; i++) { - if (!(RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_ACTIVE) & 1)) - break; - udelay(1); - } - } - data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_DOORBELL_CONTROL); - data = REG_SET_FIELD(data, CP_HQD_PQ_DOORBELL_CONTROL, - DOORBELL_EN, 0); - data = REG_SET_FIELD(data, CP_HQD_PQ_DOORBELL_CONTROL, - DOORBELL_HIT, 1); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_DOORBELL_CONTROL, data); - - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_DOORBELL_CONTROL, 0); - - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_LO, 0); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_WPTR_HI, 0); - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regCP_HQD_PQ_RPTR, 0); - - soc_v1_0_grbm_select(adev, 0, 0, 0, 0, GET_INST(GC, xcc_id)); - mutex_unlock(&adev->srbm_mutex); - - adev->mes.ring[MES_PIPE_INST(xcc_id, 0)].sched.ready = false; -} - static void mes_v12_1_kiq_dequeue(struct amdgpu_device *adev, int xcc_id) { int i; @@ -1780,10 +1646,7 @@ static int mes_v12_1_kiq_hw_init(struct amdgpu_device *adev, uint32_t xcc_id) int r = 0; struct amdgpu_ip_block *ip_block; - if (adev->enable_uni_mes) - mes_v12_1_kiq_setting(&adev->mes.ring[inst], xcc_id); - else - mes_v12_1_kiq_setting(&adev->gfx.kiq[xcc_id].ring, xcc_id); + mes_v12_1_kiq_setting(&adev->mes.ring[inst], xcc_id); if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) { @@ -1818,19 +1681,17 @@ static int mes_v12_1_kiq_hw_init(struct amdgpu_device *adev, uint32_t xcc_id) if (r) goto failure; - if (adev->enable_uni_mes) { - r = mes_v12_1_setup_coop_mode(adev, xcc_id); - if (r) - goto failure; + r = mes_v12_1_setup_coop_mode(adev, xcc_id); + if (r) + goto failure; - r = mes_v12_1_set_hw_resources(&adev->mes, - AMDGPU_MES_KIQ_PIPE, xcc_id); - if (r) - goto failure; + r = mes_v12_1_set_hw_resources(&adev->mes, + AMDGPU_MES_KIQ_PIPE, xcc_id); + if (r) + goto failure; - mes_v12_1_set_hw_resources_1(&adev->mes, - AMDGPU_MES_KIQ_PIPE, xcc_id); - } + mes_v12_1_set_hw_resources_1(&adev->mes, + AMDGPU_MES_KIQ_PIPE, xcc_id); if (adev->mes.enable_legacy_queue_map) { r = mes_v12_1_xcc_hw_init(ip_block, xcc_id); @@ -1850,12 +1711,8 @@ static int mes_v12_1_kiq_hw_fini(struct amdgpu_device *adev, uint32_t xcc_id) int inst = MES_PIPE_INST(xcc_id, AMDGPU_MES_SCHED_PIPE); if (adev->mes.ring[inst].sched.ready) { - if (adev->enable_uni_mes) - amdgpu_mes_unmap_legacy_queue(adev, - &adev->mes.ring[inst], - RESET_QUEUES, 0, 0, xcc_id); - else - mes_v12_1_kiq_dequeue_sched(adev, xcc_id); + amdgpu_mes_unmap_legacy_queue(adev, &adev->mes.ring[inst], + RESET_QUEUES, 0, 0, xcc_id); adev->mes.ring[inst].sched.ready = false; } @@ -1916,26 +1773,6 @@ static int mes_v12_1_xcc_hw_init(struct amdgpu_ip_block *ip_block, int xcc_id) if (adev->mes.ring[MES_PIPE_INST(xcc_id, 0)].sched.ready) goto out; - if (!adev->enable_mes_kiq) { - if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) { - r = mes_v12_1_load_microcode(adev, - AMDGPU_MES_SCHED_PIPE, true, xcc_id); - if (r) { - DRM_ERROR("failed to MES fw, r=%d\n", r); - return r; - } - - mes_v12_1_set_ucode_start_addr(adev, xcc_id); - - } else if (adev->firmware.load_type == - AMDGPU_FW_LOAD_RLC_BACKDOOR_AUTO) { - - mes_v12_1_set_ucode_start_addr(adev, xcc_id); - } - - mes_v12_1_enable(adev, true, xcc_id); - } - /* Enable the MES to handle doorbell ring on unmapped queue */ mes_v12_1_enable_unmapped_doorbell_handling(&adev->mes, true, xcc_id); @@ -1948,10 +1785,7 @@ static int mes_v12_1_xcc_hw_init(struct amdgpu_ip_block *ip_block, int xcc_id) if (r) goto failure; - if (adev->enable_uni_mes) { - mes_v12_1_set_hw_resources_1(&adev->mes, - AMDGPU_MES_SCHED_PIPE, xcc_id); - } + mes_v12_1_set_hw_resources_1(&adev->mes, AMDGPU_MES_SCHED_PIPE, xcc_id); mes_v12_1_init_aggregated_doorbell(&adev->mes, xcc_id); r = mes_v12_1_query_sched_status(&adev->mes, @@ -1963,12 +1797,6 @@ static int mes_v12_1_xcc_hw_init(struct amdgpu_ip_block *ip_block, int xcc_id) amdgpu_mes_validate_fw_version(adev); out: - /* - * Disable KIQ ring usage from the driver once MES is enabled. - * MES uses KIQ ring exclusively so driver cannot access KIQ ring - * with MES enabled. - */ - adev->gfx.kiq[xcc_id].ring.sched.ready = false; adev->mes.ring[MES_PIPE_INST(xcc_id, 0)].sched.ready = true; return 0; From 4aa733ab15b303e2a40e2985ac21a0e01f24cc4a Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 25 Aug 2026 23:09:32 +0800 Subject: [PATCH 005/114] drm/amd/pm: report energy accumulator for smu 14.0.3 add energy accumulator on pmfw 0x00685000 and above version. Signed-off-by: Kevin Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 56a5c11bc196..ed99e61f18e1 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -2142,6 +2142,7 @@ static void smu_v14_0_2_init_msg_ctl(struct smu_context *smu) static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, void **table) { + uint32_t mp1_ver = amdgpu_ip_version(smu->adev, MP1_HWIP, 0); struct gpu_metrics_v1_3 *gpu_metrics = (struct gpu_metrics_v1_3 *)smu_driver_table_ptr( smu, SMU_DRIVER_TABLE_GPU_METRICS); @@ -2171,6 +2172,8 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, metrics->Vcn1ActivityPercentage); gpu_metrics->average_socket_power = metrics->AverageSocketPower; + if (mp1_ver == IP_VERSION(14, 0, 3) && smu->smc_fw_version >= 0x00685000) + gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; From 8728be7e2305ac1b88cd5d9dc9c18909dc528ea5 Mon Sep 17 00:00:00 2001 From: Rafal Ostrowski Date: Tue, 4 Aug 2026 10:26:15 +0200 Subject: [PATCH 006/114] drm/amd/display: Refactor RMCM into a separate module [WHY] The RMCM logic was embedded inside the MPC block, sharing its source files, functions, register definitions and other logic. This tight coupling created several problems: - The RMCM vtable was bolted onto struct mpc_funcs, making it impossible to instantiate RMCM independently of MPC - RMCM resource acquisition had no tracking - there was no mechanism to allocate the 2 available RMCM instances to pipes or release them [HOW] RMCM is now a separate module: - dc/rmcm/ directory with per-ASIC implementations - new struct rmcm with its own funcs vtable containing shaper, 3DLUT, and fast-load operations - removed all RMCM code from MPC - added RMCM resource lifecycle management in dc_resource.c: - resource_assign_rmcm(): Assigns the instances for a whole context, so a plane that is only now asking never evicts one already using an instance - resource_release_rmcm(): Clears the pipe's allocation. - updated resource pool constructors (dcn42, dcn42b, dcn60) to create RMCM instances via the new dcnXX_rmcm_create() and destroy them in pool teardown Reviewed-by: Sridevi Arvindekar Signed-off-by: Rafal Ostrowski Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/Makefile | 1 + drivers/gpu/drm/amd/display/dc/Makefile | 2 +- drivers/gpu/drm/amd/display/dc/core/dc.c | 2 - .../drm/amd/display/dc/core/dc_hw_sequencer.c | 7 +- .../gpu/drm/amd/display/dc/core/dc_resource.c | 101 ++ .../gpu/drm/amd/display/dc/core/dc_state.c | 2 - .../gpu/drm/amd/display/dc/core/dc_stream.c | 66 -- drivers/gpu/drm/amd/display/dc/dc.h | 6 +- drivers/gpu/drm/amd/display/dc/dc_stream.h | 11 - .../amd/display/dc/hubp/dcn401/dcn401_hubp.c | 4 + .../amd/display/dc/hubp/dcn42/dcn42_hubp.c | 1 - .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 65 +- .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 4 + .../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 22 +- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 4 + .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 9 + .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 300 ++++-- .../amd/display/dc/hwss/dcn42/dcn42_hwseq.h | 15 +- .../amd/display/dc/hwss/dcn42/dcn42_init.c | 5 +- .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 122 +++ .../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 2 + .../amd/display/dc/hwss/dcn60/dcn60_init.c | 7 +- .../drm/amd/display/dc/hwss/hw_sequencer.h | 2 + .../display/dc/hwss/hw_sequencer_private.h | 4 + .../gpu/drm/amd/display/dc/inc/core_types.h | 4 +- drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h | 1 + drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h | 64 -- drivers/gpu/drm/amd/display/dc/inc/hw/rmcm.h | 140 +++ drivers/gpu/drm/amd/display/dc/inc/resource.h | 15 + .../drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c | 679 -------------- .../drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h | 714 +-------------- .../drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c | 40 - .../drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h | 238 ----- .../dc/resource/dcn42/dcn42_resource.c | 56 +- .../dc/resource/dcn42b/dcn42b_resource.c | 57 +- .../dc/resource/dcn60/dcn60_resource.c | 17 +- drivers/gpu/drm/amd/display/dc/rmcm/Makefile | 44 + .../amd/display/dc/rmcm/dcn42/dcn42_rmcm.c | 867 ++++++++++++++++++ .../amd/display/dc/rmcm/dcn42/dcn42_rmcm.h | 717 +++++++++++++++ .../amd/display/dc/rmcm/dcn60/dcn60_rmcm.c | 251 +++++ .../amd/display/dc/rmcm/dcn60/dcn60_rmcm.h | 35 + 41 files changed, 2726 insertions(+), 1977 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/inc/hw/rmcm.h create mode 100644 drivers/gpu/drm/amd/display/dc/rmcm/Makefile create mode 100644 drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.c create mode 100644 drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.h create mode 100644 drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.c create mode 100644 drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.h diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile index 0084a8d55254..96242bb2f97c 100644 --- a/drivers/gpu/drm/amd/display/Makefile +++ b/drivers/gpu/drm/amd/display/Makefile @@ -42,6 +42,7 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dwb subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/hpo subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/mmhubbub subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/mpc +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/rmcm subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/opp subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/pg subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/soc_and_ip_translator diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile index e8cdc82dcdd8..a4156b2d59b9 100644 --- a/drivers/gpu/drm/amd/display/dc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -22,7 +22,7 @@ # # Makefile for Display Core (dc) component. -DC_LIBS = basics bios dml clk_mgr dce gpio hwss irq link dsc resource optc dpp hubbub dccg hubp dio dwb hpo mmhubbub mpc opp pg +DC_LIBS = basics bios dml clk_mgr dce gpio hwss irq link dsc resource optc dpp hubbub dccg hubp dio dwb hpo mmhubbub mpc opp pg rmcm ifdef CONFIG_DRM_AMD_DC_FP diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 1ee98cd789f8..dbdaf9415dfa 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -992,8 +992,6 @@ static bool dc_construct_ctx(struct dc *dc, if (!dc_ctx) return false; - dc_stream_init_rmcm_3dlut(dc); - dc_ctx->cgs_device = init_params->cgs_device; dc_ctx->driver_context = init_params->driver; dc_ctx->dc = dc; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 4b8af67ff4b5..7262596c470e 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -1489,7 +1489,9 @@ void hwss_build_fast_sequence(struct dc *dc, if (current_mpc_pipe->plane_state->update_bits.lut_3d && current_mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable && - current_mpc_pipe->plane_state->cm.flags.bits.shaper_enable && + /* RMCM drives the 3DLUT without a shaper */ + (current_mpc_pipe->plane_state->cm.flags.bits.shaper_enable || + current_mpc_pipe->plane_state->cm.flags.bits.rmcm_enable) && current_mpc_pipe->plane_state->cm.flags.bits.lut3d_enable && current_mpc_pipe->plane_res.hubp->funcs->hubp_enable_3dlut_fl) { block_sequence[*num_steps].params.hubp_enable_3dlut_fl_params.hubp = @@ -1510,6 +1512,7 @@ void hwss_build_fast_sequence(struct dc *dc, primary_dpp_pipe->plane_res.hubp : current_mpc_pipe->plane_res.hubp, .ipp = current_mpc_pipe->plane_res.ipp, .mpc = dc->res_pool->mpc, + .rmcm = current_mpc_pipe->plane_res.rmcm, .mpcc_id = current_mpc_pipe->plane_res.mpcc_inst, .stream = current_mpc_pipe->stream, .plane_state = current_mpc_pipe->plane_state, @@ -2368,6 +2371,7 @@ void hwss_add_dpp_set_input_transfer_func(struct block_sequence_state *seq_state primary_dpp_pipe->plane_res.hubp : pipe_ctx->plane_res.hubp, .ipp = pipe_ctx->plane_res.ipp, .mpc = dc->res_pool->mpc, + .rmcm = pipe_ctx->plane_res.rmcm, .mpcc_id = pipe_ctx->plane_res.mpcc_inst, .plane_state = pipe_ctx->plane_state, .stream = pipe_ctx->stream, @@ -4644,6 +4648,7 @@ void hwss_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx) primary_dpp_pipe->plane_res.hubp : pipe_ctx->plane_res.hubp, .ipp = pipe_ctx->plane_res.ipp, .mpc = dc->res_pool->mpc, + .rmcm = pipe_ctx->plane_res.rmcm, .mpcc_id = pipe_ctx->plane_res.mpcc_inst, .plane_state = pipe_ctx->plane_state, .stream = pipe_ctx->stream, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 593c1fd537ae..b3889abab6b6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -3389,6 +3389,95 @@ static bool acquire_secondary_dpp_pipes_and_add_plane( return true; } +/* + * resource_assign_rmcm() - Assign the RMCM instances for a whole context + * + * RMCM is pre-blend, so every pipe asking for one needs its own instance. Ownership is + * tracked per instance in res_ctx->rmcm_in_use[]; the owning pipe is the one holding the + * matching plane_res.rmcm pointer. + * + * A pipe that already drives an RMCM keeps it, so a plane that is only now asking never + * evicts one that is already using an instance - it simply gets none when the pool is + * exhausted. This has to see the finished context, hence one pass over all pipes rather + * than one per appended plane. + */ +void resource_assign_rmcm( + struct dc_state *new_ctx, + const struct dc_state *cur_ctx, + const struct resource_pool *pool) +{ + struct rmcm *held[MAX_PIPES] = { NULL }; + unsigned int p; + int i; + + /* new_ctx and cur_ctx can be the same object, so snapshot before clearing */ + for (p = 0; p < pool->pipe_count && p < MAX_PIPES; p++) + held[p] = cur_ctx ? cur_ctx->res_ctx.pipe_ctx[p].plane_res.rmcm : NULL; + + for (p = 0; p < pool->pipe_count && p < MAX_PIPES; p++) + new_ctx->res_ctx.pipe_ctx[p].plane_res.rmcm = NULL; + + for (i = 0; i < MAX_RMCM_INST; i++) + new_ctx->res_ctx.rmcm_in_use[i] = false; + + /* incumbents first, so a later pipe cannot take an instance out from under them */ + for (p = 0; p < pool->pipe_count && p < MAX_PIPES; p++) { + struct pipe_ctx *pipe = &new_ctx->res_ctx.pipe_ctx[p]; + + if (!pipe->plane_state || !pipe->plane_state->cm.flags.bits.rmcm_enable) + continue; + + if (!held[p] || new_ctx->res_ctx.rmcm_in_use[held[p]->inst]) + continue; + + pipe->plane_res.rmcm = held[p]; + new_ctx->res_ctx.rmcm_in_use[held[p]->inst] = true; + } + + for (p = 0; p < pool->pipe_count && p < MAX_PIPES; p++) { + struct pipe_ctx *pipe = &new_ctx->res_ctx.pipe_ctx[p]; + + if (!pipe->plane_state || !pipe->plane_state->cm.flags.bits.rmcm_enable) + continue; + + if (pipe->plane_res.rmcm) + continue; + + for (i = 0; i < pool->res_cap->num_rmcm && i < MAX_RMCM_INST; i++) { + if (!pool->rmcm[i] || new_ctx->res_ctx.rmcm_in_use[i]) + continue; + + pipe->plane_res.rmcm = pool->rmcm[i]; + new_ctx->res_ctx.rmcm_in_use[i] = true; + break; + } + } +} + +/* + * resource_release_rmcm() - Release an RMCM instance for a pipe. + * + * Marks the instance free and NULLs the caller's pointer. + */ +void resource_release_rmcm( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct rmcm **rmcm) +{ + int i; + + if (!*rmcm) + return; + + for (i = 0; i < pool->res_cap->num_rmcm && i < MAX_RMCM_INST; i++) { + if (pool->rmcm[i] == *rmcm) { + res_ctx->rmcm_in_use[i] = false; + *rmcm = NULL; + return; + } + } +} + bool resource_append_dpp_pipes_for_plane_composition( struct dc_state *new_ctx, struct dc_state *cur_ctx, @@ -3414,6 +3503,12 @@ bool resource_append_dpp_pipes_for_plane_composition( pool, plane_state); } + /* Idempotent, so the last append of a commit leaves the final assignment behind. + * Releasing is driven by the MPC_RMCM_CNTL mirror (see dcn42_release_rmcm_from_mpcc). + */ + if (success) + resource_assign_rmcm(new_ctx, cur_ctx, pool); + return success; } @@ -3428,6 +3523,12 @@ void resource_remove_dpp_pipes_for_plane_composition( struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; if (pipe_ctx->plane_state == plane_state) { + /* Release RMCM instance for this pipe */ + if (pipe_ctx->plane_res.rmcm) + resource_release_rmcm(&context->res_ctx, + pool, + &pipe_ctx->plane_res.rmcm); + if (pipe_ctx->top_pipe) pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c index 1e4fe2f941ca..edb50c23ce24 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c @@ -467,8 +467,6 @@ enum dc_status dc_state_remove_stream( return DC_ERROR_UNEXPECTED; } - dc_stream_release_3dlut_for_stream(dc, stream); - dc_stream_release(state->streams[i]); state->stream_count--; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index c05428f52d88..8d3aacc7b96c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -1038,72 +1038,6 @@ void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream) } } -/* TODO - move to per plane ownership? */ -/* -* dc_stream_get_3dlut() -* Requirements: -* 1. Is stream already owns an RMCM instance, return it. -* 2. If it doesn't and we don't need to allocate, return NULL. -* 3. If there's a free RMCM instance, assign to stream and return it. -* 4. If no free RMCM instances, return NULL. -*/ - -struct dc_rmcm_3dlut *dc_stream_get_3dlut_for_stream( - const struct dc *dc, - const struct dc_stream_state *stream, - bool allocate_one) -{ - unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts; - - // see if one is allocated for this stream - for (unsigned int i = 0; i < num_rmcm; i++) { - if (dc->res_pool->rmcm_3dlut[i].isInUse && - dc->res_pool->rmcm_3dlut[i].stream == stream) - return &dc->res_pool->rmcm_3dlut[i]; - } - - //case: not found one, and dont need to allocate - if (!allocate_one) - return NULL; - - //see if there is an unused 3dlut, allocate - for (unsigned int i = 0; i < num_rmcm; i++) { - if (!dc->res_pool->rmcm_3dlut[i].isInUse) { - dc->res_pool->rmcm_3dlut[i].isInUse = true; - dc->res_pool->rmcm_3dlut[i].stream = stream; - return &dc->res_pool->rmcm_3dlut[i]; - } - } - - //dont have a 3dlut - return NULL; -} - - -void dc_stream_release_3dlut_for_stream( - const struct dc *dc, - const struct dc_stream_state *stream) -{ - struct dc_rmcm_3dlut *rmcm_3dlut = - dc_stream_get_3dlut_for_stream(dc, stream, false); - - if (rmcm_3dlut) { - rmcm_3dlut->isInUse = false; - rmcm_3dlut->stream = NULL; - } -} - - -void dc_stream_init_rmcm_3dlut(struct dc *dc) -{ - unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts; - - for (unsigned int i = 0; i < num_rmcm; i++) { - dc->res_pool->rmcm_3dlut[i].isInUse = false; - dc->res_pool->rmcm_3dlut[i].stream = NULL; - } -} - /* * Finds the greatest index in refresh_rate_hz that contains a value <= refresh */ diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 4ecd5699ac5e..7ca5355dbad2 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -37,6 +37,7 @@ #include "link_service_types.h" #include "grph_object_ctrl_defs.h" #include +#include #include "hwss/hw_sequencer.h" #include "inc/compressor.h" @@ -1514,11 +1515,6 @@ struct lut_mem_mapping { uint16_t size; }; -struct dc_rmcm_3dlut { - bool isInUse; - const struct dc_stream_state *stream; -}; - struct dc_3dlut { struct kref refcount; struct tetrahedral_params lut_3d; diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 934ae381e730..d9d79788a92c 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -611,17 +611,6 @@ bool dc_stream_set_gamut_remap(struct dc *dc, bool dc_stream_program_csc_matrix(struct dc *dc, struct dc_stream_state *stream); -struct dc_rmcm_3dlut *dc_stream_get_3dlut_for_stream( - const struct dc *dc, - const struct dc_stream_state *stream, - bool allocate_one); - -void dc_stream_release_3dlut_for_stream( - const struct dc *dc, - const struct dc_stream_state *stream); - -void dc_stream_init_rmcm_3dlut(struct dc *dc); - struct pipe_ctx *dc_stream_get_pipe_ctx(struct dc_stream_state *stream); void dc_dmub_update_dirty_rect(struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index a2e0bafb711a..2acdac35a382 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -126,6 +126,10 @@ static enum hubp_3dlut_fl_width hubp401_get_3dlut_fl_width( enum hubp_3dlut_fl_width width = 0; switch (size) { + case CM_LUT_SIZE_999: + ASSERT(swizzle != CM_LUT_1D_PACKED_LINEAR); + width = hubp_3dlut_fl_width_9; + break; case CM_LUT_SIZE_333333: ASSERT(swizzle != CM_LUT_1D_PACKED_LINEAR); width = hubp_3dlut_fl_width_33; diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c index 86e49eb84a54..96d3310e2c3f 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c @@ -732,7 +732,6 @@ struct hubp_funcs dcn42_hubp_funcs = { .hubp_program_3dlut_fl_crossbar = hubp42_program_3dlut_fl_crossbar, .hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done, .hubp_clear_tiling = hubp3_clear_tiling, - .hubp_program_3dlut_fl_config = hubp401_program_3dlut_fl_config, .hubp_read_reg_state = hubp3_read_reg_state }; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 5a163711498a..fefc9dea7054 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -41,6 +41,7 @@ #include "opp.h" #include "ipp.h" #include "mpc.h" +#include "rmcm.h" #include "reg_helper.h" #include "dcn10/dcn10_hubp.h" #include "dcn10/dcn10_hubbub.h" @@ -565,31 +566,32 @@ static void dcn10_log_color_state(struct dc *dc, "OUT_OFFSET", "OUT_SCALE", "FL_DONE", "SOFT_UNDERFLOW", "HARD_UNDERFLOW", "MEM_PWR_ST", "FORCE", "DIS", "MODE"}; - for (i = 0; i < pool->mpcc_count; i++) { - struct mpcc_state s = {0}; + /* Read RMCM state separately - RMCM is a shared resource (only 2 instances) */ + for (i = 0; i < MAX_RMCM_INST && i < pool->mpcc_count; i++) { + struct rmcm_state r = {0}; - pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s); - if (s.opp_id != 0xf) { + if (pool->rmcm[i] && pool->rmcm[i]->funcs->read_rmcm_state) { + pool->rmcm[i]->funcs->read_rmcm_state(pool->rmcm[i], i, &r); uint32_t values[] = { i, - s.rmcm_regs.rmcm_3dlut_size, - s.rmcm_regs.rmcm_3dlut_mode, - s.rmcm_regs.rmcm_3dlut_mode_cur, - s.rmcm_regs.rmcm_3dlut_read_sel, - s.rmcm_regs.rmcm_3dlut_30bit_en, - s.rmcm_regs.rmcm_3dlut_wr_en_mask, - s.rmcm_regs.rmcm_3dlut_ram_sel, - s.rmcm_regs.rmcm_3dlut_out_norm_factor, - s.rmcm_regs.rmcm_3dlut_fl_sel, - s.rmcm_regs.rmcm_3dlut_out_offset_r, - s.rmcm_regs.rmcm_3dlut_out_scale_r, - s.rmcm_regs.rmcm_3dlut_fl_done, - s.rmcm_regs.rmcm_3dlut_fl_soft_underflow, - s.rmcm_regs.rmcm_3dlut_fl_hard_underflow, - s.rmcm_regs.rmcm_3dlut_mem_pwr_state, - s.rmcm_regs.rmcm_3dlut_mem_pwr_force, - s.rmcm_regs.rmcm_3dlut_mem_pwr_dis, - s.rmcm_regs.rmcm_3dlut_mem_pwr_mode}; + r.regs.rmcm_3dlut_size, + r.regs.rmcm_3dlut_mode, + r.regs.rmcm_3dlut_mode_cur, + r.regs.rmcm_3dlut_read_sel, + r.regs.rmcm_3dlut_30bit_en, + r.regs.rmcm_3dlut_wr_en_mask, + r.regs.rmcm_3dlut_ram_sel, + r.regs.rmcm_3dlut_out_norm_factor, + r.regs.rmcm_3dlut_fl_sel, + r.regs.rmcm_3dlut_out_offset_r, + r.regs.rmcm_3dlut_out_scale_r, + r.regs.rmcm_3dlut_fl_done, + r.regs.rmcm_3dlut_fl_soft_underflow, + r.regs.rmcm_3dlut_fl_hard_underflow, + r.regs.rmcm_3dlut_mem_pwr_state, + r.regs.rmcm_3dlut_mem_pwr_force, + r.regs.rmcm_3dlut_mem_pwr_dis, + r.regs.rmcm_3dlut_mem_pwr_mode}; int num_elements = 19; @@ -600,17 +602,18 @@ static void dcn10_log_color_state(struct dc *dc, DTN_INFO("\n"); DTN_INFO("===== MPC RMCM Shaper =====\n"); DTN_INFO("MPCC: CNTL LUT_MODE MODE_CUR WR_EN_MASK WR_SEL OFFSET SCALE START_B START_SEG_B END_B END_BASE_B MEM_PWR_ST FORCE DIS MODE\n"); - for (i = 0; i < pool->mpcc_count; i++) { - struct mpcc_state s = {0}; + for (i = 0; i < MAX_RMCM_INST && i < pool->mpcc_count; i++) { + struct rmcm_state r = {0}; - pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s); - if (s.opp_id != 0xf) + if (pool->rmcm[i] && pool->rmcm[i]->funcs->read_rmcm_state) { + pool->rmcm[i]->funcs->read_rmcm_state(pool->rmcm[i], i, &r); DTN_INFO("[%2d]: %4xh %4xh %6xh %4x %4x %4x %4x %4x %4xh %4xh %6xh %4x %4x %4x %4x\n", - i, s.rmcm_regs.rmcm_cntl, s.rmcm_regs.rmcm_shaper_lut_mode, s.rmcm_regs.rmcm_shaper_mode_cur, - s.rmcm_regs.rmcm_shaper_lut_write_en_mask, s.rmcm_regs.rmcm_shaper_lut_write_sel, s.rmcm_regs.rmcm_shaper_offset_b, - s.rmcm_regs.rmcm_shaper_scale_b, s.rmcm_regs.rmcm_shaper_rama_exp_region_start_b, s.rmcm_regs.rmcm_shaper_rama_exp_region_start_seg_b, - s.rmcm_regs.rmcm_shaper_rama_exp_region_end_b, s.rmcm_regs.rmcm_shaper_rama_exp_region_end_base_b, s.rmcm_regs.rmcm_shaper_mem_pwr_state, - s.rmcm_regs.rmcm_shaper_mem_pwr_force, s.rmcm_regs.rmcm_shaper_mem_pwr_dis, s.rmcm_regs.rmcm_shaper_mem_pwr_mode); + i, r.regs.rmcm_cntl, r.regs.rmcm_shaper_lut_mode, r.regs.rmcm_shaper_mode_cur, + r.regs.rmcm_shaper_lut_write_en_mask, r.regs.rmcm_shaper_lut_write_sel, r.regs.rmcm_shaper_offset_b, + r.regs.rmcm_shaper_scale_b, r.regs.rmcm_shaper_rama_exp_region_start_b, r.regs.rmcm_shaper_rama_exp_region_start_seg_b, + r.regs.rmcm_shaper_rama_exp_region_end_b, r.regs.rmcm_shaper_rama_exp_region_end_base_b, r.regs.rmcm_shaper_mem_pwr_state, + r.regs.rmcm_shaper_mem_pwr_force, r.regs.rmcm_shaper_mem_pwr_dis, r.regs.rmcm_shaper_mem_pwr_mode); + } } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 4e7a005d27ef..0998106c1da0 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -719,6 +719,10 @@ void dcn20_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) struct hubp *hubp = pipe_ctx->plane_res.hubp; struct dpp *dpp = pipe_ctx->plane_res.dpp; + /* Clearing 3DLUT fast load writes to HUBP, so it must precede hubp_clk_cntl() below */ + if (hws->funcs.disable_rmcm_luts) + hws->funcs.disable_rmcm_luts(dc, pipe_ctx->plane_res.rmcm, hubp, pipe_ctx->plane_res.mpcc_inst); + dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); /* In flip immediate with pipe splitting case GSL is used for diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index 430966af3808..5de38b86cb54 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -579,10 +579,24 @@ bool dcn32_set_input_transfer_func(struct set_input_transfer_func_params *params dpp->funcs->dpp_program_gamcor_lut(dpp, pwl_params); - if (hws->funcs.set_mcm_luts) - result = hws->funcs.set_mcm_luts(params->dc, dpp, params->hubp, - params->primary_hubp, params->mpc, params->mpcc_id, - params->stream, plane_state); + /* MCM and RMCM are mutually exclusive - tear the inactive one out before programming + * the active one, which then owns the shared HUBP 3DLUT fast load. + */ + if (plane_state->cm.flags.bits.rmcm_enable && hws->funcs.set_rmcm_luts) { + if (hws->funcs.disable_mcm_luts) + hws->funcs.disable_mcm_luts(params->mpc, params->mpcc_id); + + result = hws->funcs.set_rmcm_luts(params); + } else { + if (hws->funcs.disable_rmcm_luts) + hws->funcs.disable_rmcm_luts(params->dc, params->rmcm, + params->hubp, params->mpcc_id); + + if (hws->funcs.set_mcm_luts) + result = hws->funcs.set_mcm_luts(params->dc, dpp, params->hubp, + params->primary_hubp, params->mpc, params->mpcc_id, + params->stream, plane_state); + } return result; } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 16526c66c2d1..942d3f3d9f25 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -872,9 +872,13 @@ void dcn35_enable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx, */ void dcn35_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) { + struct dce_hwseq *hws = dc->hwseq; struct hubp *hubp = pipe_ctx->plane_res.hubp; struct dpp *dpp = pipe_ctx->plane_res.dpp; + /* Clearing 3DLUT fast load writes to HUBP, so it must precede hubp_clk_cntl() below */ + if (hws->funcs.disable_rmcm_luts) + hws->funcs.disable_rmcm_luts(dc, pipe_ctx->plane_res.rmcm, hubp, pipe_ctx->plane_res.mpcc_inst); dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 3a5dde5c8120..c76754df2c38 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -457,6 +457,9 @@ bool dcn401_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, /* 1D LUT */ lut_enable = cm->flags.bits.blend_enable != 0u; + if (cm->flags.bits.rmcm_enable) + lut_enable = false; + memset(&m_lut_params, 0, sizeof(m_lut_params)); if (lut_enable) { if (cm->blend_func.type == TF_TYPE_HWPWL) @@ -483,6 +486,9 @@ bool dcn401_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, /* Shaper */ lut_enable = cm->flags.bits.shaper_enable != 0u; + if (cm->flags.bits.rmcm_enable) + lut_enable = false; + if (lut_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); if (cm->shaper_func.type == TF_TYPE_HWPWL) @@ -514,6 +520,9 @@ bool dcn401_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, /* 3DLUT */ lut_enable = cm->flags.bits.lut3d_enable != 0u; + if (cm->flags.bits.rmcm_enable) + lut_enable = false; + if (lut_enable && cm->flags.bits.lut3d_dma_enable) { /* Fast (DMA) Load Mode */ /* MPC */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 23d4786ea246..c152941c34f9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -402,112 +402,226 @@ void dcn42_program_cm_hist( plane_state->cm_hist_control, plane_state->color_space); } -static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) +/* RMCM is per-pipe and reloads through its own HUBP, unlike MCM which is shared by + * the plane's pipes and always reloads through the primary one. + */ +void dcn42_trigger_3dlut_dma_load(struct pipe_ctx *pipe_ctx) { - if (mpc->funcs->rmcm.power_on_shaper_3dlut && - mpc->funcs->rmcm.fl_3dlut_configure && - hubp->funcs->hubp_program_3dlut_fl_config) - return true; + struct hubp *hubp; + bool use_rmcm = pipe_ctx->plane_state && pipe_ctx->plane_state->cm.flags.bits.rmcm_enable; - return false; -} + if (use_rmcm) { + hubp = pipe_ctx->plane_res.hubp; + } else { + const struct pipe_ctx *primary_dpp_pipe_ctx = resource_get_primary_dpp_pipe(pipe_ctx); -bool dcn42_program_rmcm_luts( - struct dc *dc, - struct dpp *dpp, - struct hubp *hubp, - const struct dc_plane_cm *cm, - struct mpc *mpc, - int mpcc_id, - struct dc_stream_state *stream) -{ - union mcm_lut_params m_lut_params = {0}; - - struct mpc_fl_3dlut_config mpc_fl_config; - - bool bypass_rmcm_shaper = false; - // true->false when it can be allocated at DI time - struct dc_rmcm_3dlut *rmcm_3dlut = dc_stream_get_3dlut_for_stream(dc, stream, false); - - bool lut_enable = false; - bool lut_bank_a = true; - - //check to see current pipe is part of a stream with allocated rmcm 3dlut - if (!rmcm_3dlut) - return false; - - /* Determine the LUT bank currently active in HW and switch to the other - * bank to preserve double-buffering (mirrors dcn401_set_mcm_luts). - */ - if (mpc->funcs->rmcm.get_3dlut_mode) - mpc->funcs->rmcm.get_3dlut_mode(mpc, mpcc_id, &lut_enable, &lut_bank_a); - - if (lut_enable) - lut_bank_a = !lut_bank_a; - - /* Shaper */ - if (cm->flags.bits.shaper_enable) { - memset(&m_lut_params, 0, sizeof(m_lut_params)); - - if (cm->shaper_func.type == TF_TYPE_HWPWL) { - m_lut_params.pwl = &cm->shaper_func.pwl; - } else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { - ASSERT(false); - cm_helper_translate_curve_to_hw_format( - dc->ctx, - &cm->shaper_func, - &dpp->shaper_params, true); - m_lut_params.pwl = &dpp->shaper_params; - } - if (m_lut_params.pwl) { - if (mpc->funcs->rmcm.populate_lut) - mpc->funcs->rmcm.populate_lut(mpc, m_lut_params, lut_bank_a, mpcc_id); - if (mpc->funcs->rmcm.program_lut_mode) - mpc->funcs->rmcm.program_lut_mode(mpc, !bypass_rmcm_shaper, lut_bank_a, mpcc_id); - } else { - //RMCM 3dlut won't work without its shaper - return false; - } + hubp = primary_dpp_pipe_ctx ? primary_dpp_pipe_ctx->plane_res.hubp : NULL; } - /* 3DLUT */ - if (!cm->flags.bits.lut3d_dma_enable) { - /* RMCM host (non-DMA) 3DLUT load is not implemented; fail fast - * instead of silently reporting success + if (hubp && hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); +} + +/* Takes the MCM out of the pipe. The caller programs the HUBP fast load afterwards. */ +void dcn42_disable_mcm_luts(struct mpc *mpc, int mpcc_id) +{ + if (!mpc || !mpc->funcs->program_lut_mode) + return; + + mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, false, false, CM_LUT_SIZE_NONE, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_SHAPER, false, false, CM_LUT_SIZE_NONE, mpcc_id); + mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, false, false, CM_LUT_SIZE_NONE, mpcc_id); +} + +static void dcn42_detach_rmcm(struct rmcm *rmcm) +{ + if (rmcm->funcs->update_3dlut_fast_load_select) + rmcm->funcs->update_3dlut_fast_load_select(rmcm, rmcm->inst, RMCM_FL_HUBP_IDX_NONE); + + if (rmcm->funcs->connect_mpcc) + rmcm->funcs->connect_mpcc(rmcm, rmcm->inst, RMCM_MPCC_ID_NONE); +} + +/* MPC makes an MPCC bypass RMCM once more than one instance is bound to it, so drop every + * instance that could still point at this MPCC. One the driver has not programmed yet counts, + * as its MPC_RMCM_CNTL may still hold a boot value. + */ +void dcn42_release_rmcm_from_mpcc(struct dc *dc, int mpcc_id, const struct rmcm *keep) +{ + int i; + + for (i = 0; i < dc->res_pool->res_cap->num_rmcm && i < MAX_RMCM_INST; i++) { + struct rmcm *rmcm = dc->res_pool->rmcm[i]; + + if (!rmcm || !rmcm->funcs) + continue; + + if (rmcm == keep) + continue; + + /* An instance the driver has not programmed yet counts as bound, because its + * MPC_RMCM_CNTL may still hold a boot value. */ - BREAK_TO_DEBUGGER(); - return false; + if (rmcm->mpcc_id == mpcc_id || rmcm->mpcc_id == RMCM_MPCC_ID_UNKNOWN) + dcn42_detach_rmcm(rmcm); + } +} + +/* Leaves mpcc_id with no RMCM in its path. A NULL hubp keeps the shared 3DLUT fast load + * untouched, for when another block already owns that HUBP. + */ +void dcn42_disable_rmcm_luts(struct dc *dc, struct rmcm *rmcm, struct hubp *hubp, int mpcc_id) +{ + if (rmcm && rmcm->funcs) { + /* HW blocks 3DLUT writes until FL mode is cleared from HUBP on VUpdate, so + * tear fast load down while the HUBP is still clocked. + */ + if (hubp && hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, false); + } + + dcn42_detach_rmcm(rmcm); + } + + dcn42_release_rmcm_from_mpcc(dc, mpcc_id, NULL); +} + +bool dcn42_set_rmcm_luts(struct set_input_transfer_func_params *params) +{ + struct dpp *dpp_base = params->dpp; + struct hubp *hubp = params->hubp; + const struct dc_plane_cm *cm = ¶ms->plane_state->cm; + struct rmcm *rmcm = params->rmcm; + int rmcm_inst; + union rmcm_lut_params m_lut_params = {0}; + struct dc_3dlut_dma lut3d_dma; + bool lut_enable; + bool lut_bank_a; + bool rval; + bool result = true; + + /* No RMCM on this plane, so there is nothing to program */ + if (!rmcm || !rmcm->funcs) { + return true; + } + + rmcm_inst = rmcm->inst; + + /* decide LUT bank based on current in use */ + if (rmcm->funcs->get_lut_mode) { + rmcm->funcs->get_lut_mode(rmcm, MCM_LUT_SHAPER, rmcm_inst, &lut_enable, &lut_bank_a); + if (!lut_enable) { + rmcm->funcs->get_lut_mode(rmcm, MCM_LUT_3DLUT, rmcm_inst, &lut_enable, &lut_bank_a); + } } else { - if (!dc_is_rmcm_3dlut_supported(hubp, mpc)) - return false; + lut_enable = false; + lut_bank_a = true; + } - //seems to be only for the MCM - mpc_fl_config.enabled = cm->flags.bits.lut3d_enable != 0u; - mpc_fl_config.size = cm->lut3d_dma.size; - mpc_fl_config.select_lut_bank_a = lut_bank_a; - mpc_fl_config.bit_depth = 0; - mpc_fl_config.hubp_index = hubp->inst; - mpc_fl_config.bias = cm->lut3d_dma.bias; - mpc_fl_config.scale = cm->lut3d_dma.scale; + /* switch to the next bank */ + if (lut_enable) { + lut_bank_a = !lut_bank_a; + } - //1. power down the block - mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, false); + /* Shaper */ + lut_enable = cm->flags.bits.shaper_enable != 0; + if (lut_enable) { + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (cm->shaper_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->shaper_func.pwl; + else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + ASSERT(false); + rval = cm_helper_translate_curve_to_hw_format(params->plane_state->ctx, + &cm->shaper_func, + &dpp_base->shaper_params, + true); + m_lut_params.pwl = rval ? &dpp_base->shaper_params : NULL; + } + if (!m_lut_params.pwl) { + lut_enable = false; + } + } else { + lut_enable = false; + } - //2. program RMCM - 3dlut reg programming - mpc->funcs->rmcm.fl_3dlut_configure(mpc, &mpc_fl_config, mpcc_id); + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_SHAPER, lut_enable, lut_bank_a, + CM_LUT_SIZE_NONE, 0, 0, rmcm_inst); + if (lut_enable && rmcm->funcs->populate_lut) + rmcm->funcs->populate_lut(rmcm, MCM_LUT_SHAPER, m_lut_params, lut_bank_a, rmcm_inst); + + /* NOTE: Toggling from DMA->Host is not supported atomically as hardware + * blocks writes until 3DLUT FL mode is cleared from HUBP on VUpdate. + * Expectation is either option is used consistently. + */ + + /* 3DLUT */ + lut_enable = cm->flags.bits.lut3d_enable != 0; + if (lut_enable && cm->flags.bits.lut3d_dma_enable) { + /* Fast (DMA) Load Mode */ + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_3DLUT, lut_enable, lut_bank_a, + cm->lut3d_dma.size, + cm->lut3d_dma.bias, cm->lut3d_dma.scale, + rmcm_inst); + + if (rmcm->funcs->program_lut_read_write_control) + rmcm->funcs->program_lut_read_write_control(rmcm, MCM_LUT_3DLUT, lut_bank_a, true, rmcm_inst); + + if (rmcm->funcs->update_3dlut_fast_load_select) + rmcm->funcs->update_3dlut_fast_load_select(rmcm, rmcm_inst, hubp->inst); /* HUBP */ if (hubp->funcs->hubp_program_3dlut_fl_config) hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); + if (hubp->funcs->hubp_program_3dlut_fl_crossbar) + hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, cm->lut3d_dma.format); + if (hubp->funcs->hubp_program_3dlut_fl_addr) hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); - //3. power on the block - mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, true); + if (hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + } else { + /* GPU memory only supports fast load path */ + BREAK_TO_DEBUGGER(); + lut_enable = false; + result = false; + } + } else { + /* RMCM has no host load path - its 3DLUT can only be fetched by HUBP fast + * load, so asking for one without DMA is a programming error. + */ + ASSERT(!lut_enable); + result = !lut_enable; + lut_enable = false; + + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_3DLUT, lut_enable, lut_bank_a, + CM_LUT_SIZE_NONE, 0, 0, rmcm_inst); + + if (rmcm->funcs->update_3dlut_fast_load_select) + rmcm->funcs->update_3dlut_fast_load_select(rmcm, rmcm_inst, RMCM_FL_HUBP_IDX_NONE); + + /* HUBP */ + memset(&lut3d_dma, 0, sizeof(lut3d_dma)); + if (hubp->funcs->hubp_program_3dlut_fl_config) + hubp->funcs->hubp_program_3dlut_fl_config(hubp, &lut3d_dma); + + if (hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, false); } - return true; + /* Connect to the MPCC only once the LUTs are loaded. Teardown is done by + * disable_rmcm_luts, at plane teardown or when the plane switches back to MCM. + */ + dcn42_release_rmcm_from_mpcc(params->dc, params->mpcc_id, rmcm); + + if (rmcm->funcs->connect_mpcc) + rmcm->funcs->connect_mpcc(rmcm, rmcm_inst, params->mpcc_id); + + return result; } bool dcn42_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, @@ -516,25 +630,11 @@ bool dcn42_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, struct dc_plane_state *plane_state) { bool result; - const struct dc_plane_cm *cm = &plane_state->cm; /* MCM */ result = dcn401_set_mcm_luts(dc, dpp, hubp, primary_hubp, mpc, mpcc_id, stream, plane_state); - /* RMCM */ - if (cm->flags.bits.rmcm_enable && cm->flags.bits.lut3d_dma_enable) { - /* TODO - move RMCM to its own block */ - dcn42_program_rmcm_luts( - dc, - dpp, - hubp, - cm, - mpc, - mpcc_id, - stream); - } - return result; } void dcn42_hardware_release(struct dc *dc) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h index 61d1217a30df..95b8ce23286c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h @@ -20,14 +20,13 @@ bool dcn42_set_mcm_luts(struct dc *dc, struct dpp *dpp, struct hubp *hubp, struct dc_stream_state *stream, struct dc_plane_state *plane_state); -bool dcn42_program_rmcm_luts( - struct dc *dc, - struct dpp *dpp, - struct hubp *hubp, - const struct dc_plane_cm *cm, - struct mpc *mpc, - int mpcc_id, - struct dc_stream_state *stream); +void dcn42_disable_rmcm_luts(struct dc *dc, struct rmcm *rmcm, struct hubp *hubp, int mpcc_id); +void dcn42_release_rmcm_from_mpcc(struct dc *dc, int mpcc_id, const struct rmcm *keep); +void dcn42_disable_mcm_luts(struct mpc *mpc, int mpcc_id); +void dcn42_trigger_3dlut_dma_load(struct pipe_ctx *pipe_ctx); + +bool dcn42_set_rmcm_luts(struct set_input_transfer_func_params *params); + void dcn42_hardware_release(struct dc *dc); void dcn42_prepare_bandwidth( diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c index aad1885cc988..b074aa9a21d9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c @@ -29,7 +29,7 @@ static const struct hw_sequencer_funcs dcn42_funcs = { .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, - .trigger_3dlut_dma_load = dcn401_trigger_3dlut_dma_load, + .trigger_3dlut_dma_load = dcn42_trigger_3dlut_dma_load, .enable_accelerated_mode = dce110_enable_accelerated_mode, .enable_timing_synchronization = dcn10_enable_timing_synchronization, .enable_per_frame_crtc_position_reset = dcn10_enable_per_frame_crtc_position_reset, @@ -151,6 +151,9 @@ static const struct hwseq_private_funcs dcn42_private_funcs = { .set_hdr_multiplier = dcn10_set_hdr_multiplier, .wait_for_blank_complete = dcn20_wait_for_blank_complete, .set_mcm_luts = dcn42_set_mcm_luts, + .set_rmcm_luts = dcn42_set_rmcm_luts, + .disable_mcm_luts = dcn42_disable_mcm_luts, + .disable_rmcm_luts = dcn42_disable_rmcm_luts, .program_mall_pipe_config = dcn32_program_mall_pipe_config, .update_mall_sel = dcn32_update_mall_sel, .calculate_dccg_k1_k2_values = NULL, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c index 5936f389d936..443afcbce5ed 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c @@ -31,6 +31,7 @@ #include "dce110/dce110_hwseq.h" #include "dcn32/dcn32_hwseq.h" #include "dcn401/dcn401_hwseq.h" +#include "dcn42/dcn42_hwseq.h" #include "dcn50/dcn50_hwseq.h" #include "dcn60_hwseq.h" #include "dcn401/dcn401_resource.h" @@ -52,6 +53,127 @@ #define FN(reg_name, field_name) \ hws->shifts->field_name, hws->masks->field_name +bool dcn60_set_rmcm_luts(struct set_input_transfer_func_params *params) +{ + struct dpp *dpp_base = params->dpp; + struct hubp *hubp = params->hubp; + const struct dc_plane_cm *cm = ¶ms->plane_state->cm; + struct rmcm *rmcm = params->rmcm; + int rmcm_inst; + union rmcm_lut_params m_lut_params = {0}; + struct dc_3dlut_dma lut3d_dma; + bool lut_enable; + /* DCN60 has a single LUT SRAM per RMCM - always bank A. */ + bool rval; + bool result = true; + + /* No RMCM on this plane, so there is nothing to program */ + if (!rmcm || !rmcm->funcs) { + return true; + } + + rmcm_inst = rmcm->inst; + + /* Shaper */ + lut_enable = cm->flags.bits.shaper_enable != 0; + if (lut_enable) { + memset(&m_lut_params, 0, sizeof(m_lut_params)); + if (cm->shaper_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->shaper_func.pwl; + else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + ASSERT(false); + rval = cm_helper_translate_curve_to_hw_format(params->plane_state->ctx, + &cm->shaper_func, + &dpp_base->shaper_params, + true); + m_lut_params.pwl = rval ? &dpp_base->shaper_params : NULL; + } + if (!m_lut_params.pwl) { + lut_enable = false; + } + } else { + lut_enable = false; + } + + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_SHAPER, lut_enable, true, + CM_LUT_SIZE_NONE, 0, 0, rmcm_inst); + if (lut_enable && rmcm->funcs->populate_lut) + rmcm->funcs->populate_lut(rmcm, MCM_LUT_SHAPER, m_lut_params, true, rmcm_inst); + + /* NOTE: Toggling from DMA->Host is not supported atomically as hardware + * blocks writes until 3DLUT FL mode is cleared from HUBP on VUpdate. + * Expectation is either option is used consistently. + */ + + /* 3DLUT */ + lut_enable = cm->flags.bits.lut3d_enable != 0; + if (lut_enable && cm->flags.bits.lut3d_dma_enable) { + /* Fast (DMA) Load Mode */ + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_3DLUT, lut_enable, true, + cm->lut3d_dma.size, + cm->lut3d_dma.bias, cm->lut3d_dma.scale, + rmcm_inst); + + if (rmcm->funcs->program_lut_read_write_control) + rmcm->funcs->program_lut_read_write_control(rmcm, MCM_LUT_3DLUT, true, true, rmcm_inst); + + if (rmcm->funcs->update_3dlut_fast_load_select) + rmcm->funcs->update_3dlut_fast_load_select(rmcm, rmcm_inst, hubp->inst); + + /* HUBP */ + if (hubp->funcs->hubp_program_3dlut_fl_config) + hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); + + if (hubp->funcs->hubp_program_3dlut_fl_crossbar) + hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, cm->lut3d_dma.format); + + if (hubp->funcs->hubp_program_3dlut_fl_addr) + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); + + if (hubp->funcs->hubp_enable_3dlut_fl) { + hubp->funcs->hubp_enable_3dlut_fl(hubp, true); + } else { + /* GPU memory only supports fast load path */ + BREAK_TO_DEBUGGER(); + result = false; + } + } else { + /* RMCM 3DLUT is fetched only by HUBP fast load, so a host load request is a + * programming error. + */ + ASSERT(!lut_enable); + result = !lut_enable; + lut_enable = false; + + if (rmcm->funcs->program_lut_mode) + rmcm->funcs->program_lut_mode(rmcm, MCM_LUT_3DLUT, lut_enable, true, + CM_LUT_SIZE_NONE, 0, 0, rmcm_inst); + + if (rmcm->funcs->update_3dlut_fast_load_select) + rmcm->funcs->update_3dlut_fast_load_select(rmcm, rmcm_inst, RMCM_FL_HUBP_IDX_NONE); + + /* HUBP */ + memset(&lut3d_dma, 0, sizeof(lut3d_dma)); + if (hubp->funcs->hubp_program_3dlut_fl_config) + hubp->funcs->hubp_program_3dlut_fl_config(hubp, &lut3d_dma); + + if (hubp->funcs->hubp_enable_3dlut_fl) + hubp->funcs->hubp_enable_3dlut_fl(hubp, false); + } + + /* Connect to the MPCC only once the LUTs are loaded. Teardown is done by + * disable_rmcm_luts, at plane teardown or when the plane switches back to MCM. + */ + dcn42_release_rmcm_from_mpcc(params->dc, params->mpcc_id, rmcm); + + if (rmcm->funcs->connect_mpcc) + rmcm->funcs->connect_mpcc(rmcm, rmcm_inst, params->mpcc_id); + + return result; +} + static void dcn60_build_audio_output( struct dc_state *state, const struct pipe_ctx *pipe_ctx, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h index 3a34150096df..b880988ff1fc 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h @@ -30,4 +30,6 @@ void dcn60_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx void dcn60_program_perfmon(struct dc *dc, struct dc_state *context); bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable); +bool dcn60_set_rmcm_luts(struct set_input_transfer_func_params *params); + #endif /* __DC_HWSS_DCN60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c index 37e88d45409b..d2fae89dfa05 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c @@ -28,7 +28,7 @@ static const struct hw_sequencer_funcs dcn60_funcs = { .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, - .trigger_3dlut_dma_load = dcn401_trigger_3dlut_dma_load, + .trigger_3dlut_dma_load = dcn42_trigger_3dlut_dma_load, .enable_accelerated_mode = dce110_enable_accelerated_mode, .enable_timing_synchronization = dcn10_enable_timing_synchronization, .enable_per_frame_crtc_position_reset = dcn10_enable_per_frame_crtc_position_reset, @@ -147,7 +147,7 @@ static const struct hwseq_private_funcs dcn60_private_funcs = { .init_blank = dcn32_init_blank, .disable_vga = NULL, .bios_golden_init = dcn10_bios_golden_init, - .plane_atomic_disable = dcn20_plane_atomic_disable, + .plane_atomic_disable = dcn35_plane_atomic_disable, .plane_atomic_power_down = dcn401_plane_atomic_power_down, .plane_atomic_power_down_sequence = dcn401_plane_atomic_power_down_sequence, .enable_power_gating_plane = NULL, @@ -162,6 +162,9 @@ static const struct hwseq_private_funcs dcn60_private_funcs = { .set_hdr_multiplier_sequence = dcn401_set_hdr_multiplier_sequence, .wait_for_blank_complete = dcn20_wait_for_blank_complete, .set_mcm_luts = dcn42_set_mcm_luts, + .set_rmcm_luts = dcn60_set_rmcm_luts, + .disable_mcm_luts = dcn42_disable_mcm_luts, + .disable_rmcm_luts = dcn42_disable_rmcm_luts, .program_mall_pipe_config = NULL, .update_mall_sel = NULL, .calculate_dccg_k1_k2_values = NULL, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 0133c6156897..4cf61eb647dd 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -46,6 +46,7 @@ struct resource_pool; struct dc_phy_addr_space_config; struct dc_virtual_addr_space_config; struct dpp; +struct rmcm; struct dce_hwseq; struct link_resource; struct dc_dmub_cmd; @@ -101,6 +102,7 @@ struct set_input_transfer_func_params { struct hubp *hubp; struct hubp *primary_hubp; struct mpc *mpc; + struct rmcm *rmcm; int mpcc_id; struct dc_stream_state *stream; struct input_pixel_processor *ipp; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h index 9730f51cf3c0..b6932b9bc206 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h @@ -73,6 +73,7 @@ struct timing_generator; struct tg_color; struct output_pixel_processor; struct mpcc_blnd_cfg; +struct rmcm; struct hwseq_private_funcs { @@ -193,6 +194,9 @@ struct hwseq_private_funcs { struct hubp *primary_hubp, struct mpc *mpc, int mpcc_id, struct dc_stream_state *stream, struct dc_plane_state *plane_state); + bool (*set_rmcm_luts)(struct set_input_transfer_func_params *params); + void (*disable_mcm_luts)(struct mpc *mpc, int mpcc_id); + void (*disable_rmcm_luts)(struct dc *dc, struct rmcm *rmcm, struct hubp *hubp, int mpcc_id); void (*PLAT_58856_wa)(struct dc_state *context, struct pipe_ctx *pipe_ctx); void (*setup_hpo_hw_control)(const struct dce_hwseq *hws, bool enable); diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index d2cca2c97a41..6563eaaa0c0c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -308,7 +308,7 @@ struct resource_pool { struct hpo_dp_link_encoder *hpo_dp_link_enc[MAX_HPO_DP2_LINK_ENCODERS]; struct dc_3dlut *mpc_lut[MAX_PIPES]; struct dc_transfer_func *mpc_shaper[MAX_PIPES]; - struct dc_rmcm_3dlut rmcm_3dlut[MAX_RMCM_INST]; + struct rmcm *rmcm[MAX_RMCM_INST]; struct { unsigned int xtalin_clock_inKhz; @@ -401,6 +401,7 @@ struct plane_resource { struct transform *xfm; struct dpp *dpp; uint8_t mpcc_inst; + struct rmcm *rmcm; struct dcn_fe_bandwidth bw; }; @@ -558,6 +559,7 @@ struct resource_context { unsigned int hpo_dp_link_enc_to_link_idx[MAX_HPO_DP2_LINK_ENCODERS]; int hpo_dp_link_enc_ref_cnts[MAX_HPO_DP2_LINK_ENCODERS]; bool is_mpc_3dlut_acquired[MAX_PIPES]; + bool rmcm_in_use[MAX_RMCM_INST]; /* used to build scalar data in dml2 and for edp backlight programming */ struct pipe_ctx temp_pipe; }; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index 9dc9317c28a4..b97f08b20984 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -87,6 +87,7 @@ enum hubp_3dlut_fl_addressing_mode { }; enum hubp_3dlut_fl_width { + hubp_3dlut_fl_width_9 = 9, hubp_3dlut_fl_width_17 = 17, hubp_3dlut_fl_width_33 = 33, hubp_3dlut_fl_width_17_transformed = 4916, //mpc default diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h index d863f4bdccad..d19f90a5893e 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h @@ -193,42 +193,6 @@ struct mpc_grph_gamut_adjustment { enum mpcc_gamut_remap_id mpcc_gamut_remap_block_id; }; -struct mpc_rmcm_regs { - uint32_t rmcm_3dlut_mem_pwr_state; - uint32_t rmcm_3dlut_mem_pwr_force; - uint32_t rmcm_3dlut_mem_pwr_dis; - uint32_t rmcm_3dlut_mem_pwr_mode; - uint32_t rmcm_3dlut_size; - uint32_t rmcm_3dlut_mode; - uint32_t rmcm_3dlut_mode_cur; - uint32_t rmcm_3dlut_read_sel; - uint32_t rmcm_3dlut_30bit_en; - uint32_t rmcm_3dlut_wr_en_mask; - uint32_t rmcm_3dlut_ram_sel; - uint32_t rmcm_3dlut_out_norm_factor; - uint32_t rmcm_3dlut_fl_sel; - uint32_t rmcm_3dlut_out_offset_r; - uint32_t rmcm_3dlut_out_scale_r; - uint32_t rmcm_3dlut_fl_done; - uint32_t rmcm_3dlut_fl_soft_underflow; - uint32_t rmcm_3dlut_fl_hard_underflow; - uint32_t rmcm_cntl; - uint32_t rmcm_shaper_mem_pwr_state; - uint32_t rmcm_shaper_mem_pwr_force; - uint32_t rmcm_shaper_mem_pwr_dis; - uint32_t rmcm_shaper_mem_pwr_mode; - uint32_t rmcm_shaper_lut_mode; - uint32_t rmcm_shaper_mode_cur; - uint32_t rmcm_shaper_lut_write_en_mask; - uint32_t rmcm_shaper_lut_write_sel; - uint32_t rmcm_shaper_offset_b; - uint32_t rmcm_shaper_scale_b; - uint32_t rmcm_shaper_rama_exp_region_start_b; - uint32_t rmcm_shaper_rama_exp_region_start_seg_b; - uint32_t rmcm_shaper_rama_exp_region_end_b; - uint32_t rmcm_shaper_rama_exp_region_end_base_b; -}; - struct mpcc_sm_cfg { bool enable; /* 0-single plane,2-row subsampling,4-column subsampling,6-checkboard subsampling */ @@ -340,7 +304,6 @@ struct mpcc_state { uint32_t rgam_mode; uint32_t rgam_lut; struct mpc_grph_gamut_adjustment gamut_remap; - struct mpc_rmcm_regs rmcm_regs; }; struct dcn_mpc_reg_state { @@ -1143,33 +1106,6 @@ struct mpc_funcs { const int mpcc_id, bool *enable, bool *lut_bank_a); - - /** - * @rmcm: - * - * MPC RMCM new HW sequential programming functions - */ - struct { - void (*fl_3dlut_configure)(struct mpc *mpc, struct mpc_fl_3dlut_config *cfg, int mpcc_id); - void (*enable_3dlut_fl)(struct mpc *mpc, bool enable, int mpcc_id); - void (*update_3dlut_fast_load_select)(struct mpc *mpc, int mpcc_id, int hubp_idx); - void (*program_lut_read_write_control)(struct mpc *mpc, const enum MCM_LUT_ID id, - bool lut_bank_a, bool enabled, int mpcc_id); - void (*program_lut_mode)(struct mpc *mpc, - bool enable, - bool lut_bank_a, - int mpcc_id); - void (*program_3dlut_size)(struct mpc *mpc, const enum dc_cm_lut_size size, int mpcc_id); - void (*program_bias_scale)(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id); - void (*program_bit_depth)(struct mpc *mpc, uint16_t bit_depth, int mpcc_id); - bool (*is_config_supported)(uint32_t width); - - void (*power_on_shaper_3dlut)(struct mpc *mpc, uint32_t mpcc_id, bool power_on); - void (*populate_lut)(struct mpc *mpc, const union mcm_lut_params params, - bool lut_bank_a, int mpcc_id); - void (*get_3dlut_mode)(struct mpc *mpc, int mpcc_id, - bool *enable, bool *lut_bank_a); - } rmcm; }; #endif diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/rmcm.h b/drivers/gpu/drm/amd/display/dc/inc/hw/rmcm.h new file mode 100644 index 000000000000..636cd0970438 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/rmcm.h @@ -0,0 +1,140 @@ +/* + * 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 + * + */ + +#ifndef __DC_RMCM_H__ +#define __DC_RMCM_H__ + +#include "dc_types.h" + +#define MAX_RMCM 2 + +/* Detach values for the RMCM binding registers: no MPCC connected (MPC_RMCM_CNTL) + * and no HUBP driving 3DLUT fast load (MPC_RMCM_3DLUT_FL_SEL). + */ +#define RMCM_MPCC_ID_NONE 0xF +#define RMCM_FL_HUBP_IDX_NONE 0xF + +/* MPC_RMCM_CNTL has not been written by the driver yet, so it may still hold + * whatever the boot state left there. + */ +#define RMCM_MPCC_ID_UNKNOWN (-1) + +/* MCM_LUT_ID is defined in mpc.h */ +enum MCM_LUT_ID; + +union rmcm_lut_params { + const struct pwl_params *pwl; + const struct tetrahedral_params *lut3d; +}; + +struct rmcm_regs { + uint32_t rmcm_3dlut_mem_pwr_state; + uint32_t rmcm_3dlut_mem_pwr_force; + uint32_t rmcm_3dlut_mem_pwr_dis; + uint32_t rmcm_3dlut_mem_pwr_mode; + uint32_t rmcm_3dlut_size; + uint32_t rmcm_3dlut_mode; + uint32_t rmcm_3dlut_mode_cur; + uint32_t rmcm_3dlut_read_sel; + uint32_t rmcm_3dlut_30bit_en; + uint32_t rmcm_3dlut_wr_en_mask; + uint32_t rmcm_3dlut_ram_sel; + uint32_t rmcm_3dlut_out_norm_factor; + uint32_t rmcm_3dlut_fl_sel; + uint32_t rmcm_3dlut_out_offset_r; + uint32_t rmcm_3dlut_out_scale_r; + uint32_t rmcm_3dlut_fl_done; + uint32_t rmcm_3dlut_fl_soft_underflow; + uint32_t rmcm_3dlut_fl_hard_underflow; + uint32_t rmcm_cntl; + uint32_t rmcm_shaper_mem_pwr_state; + uint32_t rmcm_shaper_mem_pwr_force; + uint32_t rmcm_shaper_mem_pwr_dis; + uint32_t rmcm_shaper_mem_pwr_mode; + uint32_t rmcm_shaper_lut_mode; + uint32_t rmcm_shaper_mode_cur; + uint32_t rmcm_shaper_lut_write_en_mask; + uint32_t rmcm_shaper_lut_write_sel; + uint32_t rmcm_shaper_offset_b; + uint32_t rmcm_shaper_scale_b; + uint32_t rmcm_shaper_rama_exp_region_start_b; + uint32_t rmcm_shaper_rama_exp_region_start_seg_b; + uint32_t rmcm_shaper_rama_exp_region_end_b; + uint32_t rmcm_shaper_rama_exp_region_end_base_b; +}; + +/** + * struct rmcm_state - RMCM hardware state + * + * Captures the current hardware state of a Relocatable MCM instance. + * Used for debugging, state readback, and hardware validation. + */ +struct rmcm_state { + /** + * @regs: Register state snapshot + */ + struct rmcm_regs regs; +}; + +/** + * struct rmcm - RMCM hardware instance + * + * Represents a single RMCM (Relocatable MCM) hardware instance. + * DCN 4.2+ has 2 RMCM instances that are shared across streams. + */ +struct rmcm { + struct dc_context *ctx; + int inst; + /* MPCC bound through MPC_RMCM_CNTL; MPC bypasses RMCM entirely when two + * instances are bound to the same MPCC. + */ + int mpcc_id; + const struct rmcm_funcs *funcs; +}; + +/** + * struct rmcm_funcs - RMCM hardware abstraction function pointers + * + * Function pointers for RMCM (Relocatable MCM) hardware operations. + * These provide the interface for programming the shared cross-stream + * color manager instances. + */ +struct rmcm_funcs { + void (*get_lut_mode)(struct rmcm *rmcm, const enum MCM_LUT_ID id, int rmcm_id, bool *enable, bool *lut_bank_a); + void (*program_lut_mode)(struct rmcm *rmcm, const enum MCM_LUT_ID id, + bool enable, bool lut_bank_a, const enum dc_cm_lut_size size, + uint16_t bias, uint16_t scale, int rmcm_id); + void (*populate_lut)(struct rmcm *rmcm, const enum MCM_LUT_ID id, + const union rmcm_lut_params params, bool lut_bank_a, int rmcm_id); + void (*program_lut_read_write_control)(struct rmcm *rmcm, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int rmcm_id); + void (*update_3dlut_fast_load_select)(struct rmcm *rmcm, int rmcm_id, int hubp_idx); + void (*get_3dlut_fast_load_status)(struct rmcm *rmcm, int rmcm_id, + bool *done, bool *soft_underflow, bool *hard_underflow); + void (*read_rmcm_state)(struct rmcm *rmcm, int rmcm_id, struct rmcm_state *s); + void (*connect_mpcc)(struct rmcm *rmcm, int rmcm_id, int mpcc_id); +}; + +#endif // __DC_RMCM_H__ diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 9c6794f8dec0..21a6f90956e8 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -336,6 +336,21 @@ void resource_remove_dpp_pipes_for_plane_composition( const struct resource_pool *pool, const struct dc_plane_state *plane_state); +/* + * RMCM resource management. Instances are handed out for a whole context at once so that a + * plane that is only now asking cannot evict one that is already using an instance. + * Tracking is via res_ctx->rmcm_in_use[]. + */ +void resource_assign_rmcm( + struct dc_state *new_ctx, + const struct dc_state *cur_ctx, + const struct resource_pool *pool); + +void resource_release_rmcm( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct rmcm **rmcm); + /* * Update ODM slice count by acquiring or releasing pipes. If new slices need * to be added, it is going to add them to the last ODM index. If existing diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c index 5a888e1e1cc5..0123a9fcfef5 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c @@ -63,680 +63,14 @@ void mpc42_update_blending( mpcc->blnd_cfg = *blnd_cfg; } -/* RMCM Shaper functions */ -void mpc42_power_on_rmcm_shaper_3dlut( - struct mpc *mpc, - uint32_t mpcc_id, - bool power_on) -{ - uint32_t power_status_shaper = 2; - uint32_t power_status_3dlut = 2; - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - int max_retries = 10; - REG_SET(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], 0, - MPC_RMCM_3DLUT_MEM_PWR_DIS, power_on == true ? 0 : 1); - REG_SET(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], 0, - MPC_RMCM_SHAPER_MEM_PWR_DIS, power_on == true ? 0 : 1); - /* wait for memory to fully power up */ - if (power_on && mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) { - REG_WAIT(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], MPC_RMCM_SHAPER_MEM_PWR_STATE, 0, 1, max_retries); - REG_WAIT(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], MPC_RMCM_3DLUT_MEM_PWR_STATE, 0, 1, max_retries); - } - - /*read status is not mandatory, it is just for debugging*/ - REG_GET(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], MPC_RMCM_SHAPER_MEM_PWR_STATE, &power_status_shaper); - REG_GET(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], MPC_RMCM_3DLUT_MEM_PWR_STATE, &power_status_3dlut); - - if (power_status_shaper != 0 && power_on == true) - BREAK_TO_DEBUGGER(); - - if (power_status_3dlut != 0 && power_on == true) - BREAK_TO_DEBUGGER(); -} - -void mpc42_configure_rmcm_shaper_lut( - struct mpc *mpc, - bool is_ram_a, - uint32_t mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_UPDATE(MPC_RMCM_SHAPER_SCALE_G_B[mpcc_id], - MPC_RMCM_SHAPER_SCALE_B, 0x7000); - REG_UPDATE(MPC_RMCM_SHAPER_SCALE_G_B[mpcc_id], - MPC_RMCM_SHAPER_SCALE_G, 0x7000); - REG_UPDATE(MPC_RMCM_SHAPER_SCALE_R[mpcc_id], - MPC_RMCM_SHAPER_SCALE_R, 0x7000); - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_SEL, is_ram_a == true ? 0:1); - REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[mpcc_id], 0, MPC_RMCM_SHAPER_LUT_INDEX, 0); -} - -void mpc42_program_rmcm_shaper_luta_settings( - struct mpc *mpc, - const struct pwl_params *params, - uint32_t mpcc_id) -{ - const struct gamma_curve *curve; - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_B[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].blue.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); - REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_G[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].green.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); - REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_R[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].red.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); - - REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_B[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].blue.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].blue.custom_float_y); - REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_G[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].green.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].green.custom_float_y); - REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_R[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].red.custom_float_x, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].red.custom_float_y); - - curve = params->arr_curve_points; - if (curve) { - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_0_1[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_2_3[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_4_5[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_6_7[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_8_9[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_10_11[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_12_13[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_14_15[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_16_17[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_18_19[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_20_21[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_22_23[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_24_25[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_26_27[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_28_29[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_30_31[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_32_33[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - } -} - - -void mpc42_program_rmcm_shaper_lutb_settings( - struct mpc *mpc, - const struct pwl_params *params, - uint32_t mpcc_id) -{ - const struct gamma_curve *curve; - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_B[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].blue.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); - REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_G[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].green.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); - REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_R[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].red.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); - - REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_B[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].blue.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].blue.custom_float_y); - REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_G[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].green.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].green.custom_float_y); - REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_R[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].red.custom_float_x, - MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].red.custom_float_y); - - curve = params->arr_curve_points; - if (curve) { - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_0_1[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_2_3[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_4_5[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_6_7[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_8_9[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_10_11[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_12_13[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_14_15[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_16_17[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_18_19[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_20_21[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_22_23[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_24_25[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_26_27[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_28_29[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_30_31[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - - curve += 2; - REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_32_33[mpcc_id], 0, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, - MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); - } -} - -void mpc42_program_rmcm_shaper_lut( - struct mpc *mpc, - const struct pwl_result_data *rgb, - uint32_t num, - uint32_t mpcc_id) -{ - uint32_t i, red, green, blue; - uint32_t red_delta, green_delta, blue_delta; - uint32_t red_value, green_value, blue_value; - - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - for (i = 0; i < num; i++) { - - red = rgb[i].red_reg; - green = rgb[i].green_reg; - blue = rgb[i].blue_reg; - - red_delta = rgb[i].delta_red_reg; - green_delta = rgb[i].delta_green_reg; - blue_delta = rgb[i].delta_blue_reg; - - red_value = ((red_delta & 0x3ff) << 14) | (red & 0x3fff); - green_value = ((green_delta & 0x3ff) << 14) | (green & 0x3fff); - blue_value = ((blue_delta & 0x3ff) << 14) | (blue & 0x3fff); - - REG_SET(MPC_RMCM_SHAPER_LUT_DATA[mpcc_id], 0, MPC_RMCM_SHAPER_LUT_DATA, red_value); - REG_SET(MPC_RMCM_SHAPER_LUT_DATA[mpcc_id], 0, MPC_RMCM_SHAPER_LUT_DATA, green_value); - REG_SET(MPC_RMCM_SHAPER_LUT_DATA[mpcc_id], 0, MPC_RMCM_SHAPER_LUT_DATA, blue_value); - } -} - -void mpc42_enable_3dlut_fl(struct mpc *mpc, bool enable, int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - //if enabled cho0se mpc 0, else: off (default value) - REG_UPDATE(MPC_RMCM_CNTL[mpcc_id], MPC_RMCM_CNTL, enable ? 0 : 0xF); //0xF is not connected - - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], MPC_RMCM_3DLUT_WRITE_EN_MASK, 0); - - REG_UPDATE(MPC_RMCM_MEM_PWR_CTRL[mpcc_id], MPC_RMCM_3DLUT_MEM_PWR_DIS, enable ? 0 : 3); -} - -void mpc42_update_3dlut_fast_load_select(struct mpc *mpc, int mpcc_id, int hubp_idx) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_SET(MPC_RMCM_3DLUT_FAST_LOAD_SELECT[mpcc_id], 0, - MPC_RMCM_3DLUT_FL_SEL, - hubp_idx); -} - -void mpc42_populate_rmcm_lut(struct mpc *mpc, const union mcm_lut_params params, - bool lut_bank_a, int mpcc_id) -{ - const enum dc_lut_mode next_mode = lut_bank_a ? LUT_RAM_A : LUT_RAM_B; - const struct pwl_params *lut_shaper = params.pwl; - - if (lut_shaper == NULL) - return; - if (mpc->ctx->dc->debug.enable_mem_low_power.bits.mpc) - mpc42_power_on_rmcm_shaper_3dlut(mpc, mpcc_id, true); - - mpc42_configure_rmcm_shaper_lut(mpc, next_mode == LUT_RAM_A, mpcc_id); - - if (next_mode == LUT_RAM_A) - mpc42_program_rmcm_shaper_luta_settings(mpc, lut_shaper, mpcc_id); - else - mpc42_program_rmcm_shaper_lutb_settings(mpc, lut_shaper, mpcc_id); - - mpc42_program_rmcm_shaper_lut( - mpc, lut_shaper->rgb_resulted, lut_shaper->hw_points_num, mpcc_id); - - mpc42_power_on_rmcm_shaper_3dlut(mpc, mpcc_id, false); -} - -void mpc42_program_rmcm_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, - bool lut_bank_a, bool enabled, int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - switch (id) { - case MCM_LUT_3DLUT: - REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_MODE, - (!enabled) ? 0 : - (lut_bank_a) ? 1 : 2); - - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPC_RMCM_3DLUT_RAM_SEL, - (lut_bank_a) ? 0 : 1); - break; - case MCM_LUT_SHAPER: - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); - - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_SEL, - lut_bank_a == true ? 0:1); - - REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[mpcc_id], 0, - MPC_RMCM_SHAPER_LUT_INDEX, 0); - break; - default: - break; - } -} - -void mpc42_program_lut_mode(struct mpc *mpc, - bool enable, - bool lut_bank_a, - int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - if (enable) { - REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[mpcc_id], MPC_RMCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); - } else { - REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[mpcc_id], MPC_RMCM_SHAPER_LUT_MODE, 0); - } -} - -static uint32_t mpc42_get_rmcm_3dlut_width( - const enum dc_cm_lut_size size) -{ - uint32_t width = 0; - - switch (size) { - case CM_LUT_SIZE_333333: - width = 2; - break; - case CM_LUT_SIZE_171717: - default: - width = 0; - break; - } - - return width; -} - -void mpc42_program_rmcm_3dlut_size(struct mpc *mpc, - const enum dc_cm_lut_size size, - int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - uint32_t width = mpc42_get_rmcm_3dlut_width(size); - - REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], - MPC_RMCM_3DLUT_SIZE, width); -} - -void mpc42_program_rmcm_3dlut_fast_load_bias_scale(struct mpc *mpc, uint16_t bias, uint16_t scale, int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_R[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_R, bias, - MPC_RMCM_3DLUT_OUT_SCALE_R, scale); - - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_G[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_G, bias, - MPC_RMCM_3DLUT_OUT_SCALE_G, scale); - - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_B[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_B, bias, - MPC_RMCM_3DLUT_OUT_SCALE_B, scale); -} - -void mpc42_program_rmcm_bit_depth(struct mpc *mpc, uint16_t bit_depth, int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], MPC_RMCM_3DLUT_WRITE_EN_MASK, 0xF); - - //program bit_depth - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPC_RMCM_3DLUT_30BIT_EN, - (bit_depth == 10) ? 1 : 0); -} - -void mpc42_set_fl_config( - struct mpc *mpc, - struct mpc_fl_3dlut_config *cfg, - int mpcc_id) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - - uint32_t width = mpc42_get_rmcm_3dlut_width(cfg->size); - /* - From: Jie Zhou - - To program any of the memories content. The following sequence is used. - Set the MPCC_OGAM/SHAPER/3DLUT/1DLUT_PWR_DIS to 1 (Only need to set the one - that is being programmed) Set DISPCLK_G_PIPE_GATE_DISABLE to 1 for the - MPCC pipe that’s being used, so the memory’s clock is ungated. Program the - target memory. Set the MPCC_OGAM/SHAPER/3DLUT/1DLUT_PWR_DIS back to 0. - Set DISPCLK_G_PIPE_GATE_DISABLE back to 0 - */ - - //disconnect fl from mpc - REG_SET(MPCC_MCM_3DLUT_FAST_LOAD_SELECT[mpcc_id], 0, - MPCC_MCM_3DLUT_FL_SEL, 0xF); - - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPC_RMCM_3DLUT_WRITE_EN_MASK, 0xF); - - //program bit_depth - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPC_RMCM_3DLUT_30BIT_EN, (cfg->bit_depth == 10) ? 1 : 0); - - REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_id], - MPC_RMCM_3DLUT_RAM_SEL, (cfg->select_lut_bank_a) ? 0 : 1); - - //bias and scale - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_R[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_R, cfg->bias, - MPC_RMCM_3DLUT_OUT_SCALE_R, cfg->scale); - - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_G[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_G, cfg->bias, - MPC_RMCM_3DLUT_OUT_SCALE_G, cfg->scale); - - REG_UPDATE_2(MPC_RMCM_3DLUT_OUT_OFFSET_B[mpcc_id], - MPC_RMCM_3DLUT_OUT_OFFSET_B, cfg->bias, - MPC_RMCM_3DLUT_OUT_SCALE_B, cfg->scale); - - //width - REG_UPDATE_2(MPC_RMCM_3DLUT_MODE[mpcc_id], - MPC_RMCM_3DLUT_SIZE, width, - MPC_RMCM_3DLUT_MODE, (!cfg->enabled) ? 0 : (cfg->select_lut_bank_a) ? 1 : 2); - - //connect to hubp - REG_SET(MPC_RMCM_3DLUT_FAST_LOAD_SELECT[mpcc_id], 0, - MPC_RMCM_3DLUT_FL_SEL, cfg->hubp_index); - - //ENABLE - //if enabled pick mpc 0, else: off (0xF) - //in future we'll select specific MPC - REG_UPDATE(MPC_RMCM_CNTL[mpcc_id], MPC_RMCM_CNTL, cfg->enabled ? 0 : 0xF); -} - -void mpc42_get_rmcm_3dlut_mode( - struct mpc *mpc, - int mpcc_id, - bool *enable, - bool *lut_bank_a) -{ - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - uint32_t mode_current = 0; - - REG_GET(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_MODE_CURRENT, &mode_current); - - /* MPC_RMCM_3DLUT_MODE encoding: - * 0 -> disabled, 1 -> bank A, 2 -> bank B - */ - *enable = mode_current != 0; - *lut_bank_a = mode_current != 2; -} void mpc42_read_mpcc_state( struct mpc *mpc, int mpcc_inst, struct mpcc_state *s) { - struct dcn42_mpc *mpc42 = TO_DCN42_MPC(mpc); - mpc1_read_mpcc_state(mpc, mpcc_inst, s); - - if (mpcc_inst < 2) { - /* RMCM 3DLUT Status */ - REG_GET_4(MPC_RMCM_MEM_PWR_CTRL[mpcc_inst], MPC_RMCM_3DLUT_MEM_PWR_FORCE, &s->rmcm_regs.rmcm_3dlut_mem_pwr_force, - MPC_RMCM_3DLUT_MEM_PWR_DIS, &s->rmcm_regs.rmcm_3dlut_mem_pwr_dis, - MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, &s->rmcm_regs.rmcm_3dlut_mem_pwr_mode, - MPC_RMCM_3DLUT_MEM_PWR_STATE, &s->rmcm_regs.rmcm_3dlut_mem_pwr_state); - - REG_GET_3(MPC_RMCM_3DLUT_MODE[mpcc_inst], MPC_RMCM_3DLUT_SIZE, &s->rmcm_regs.rmcm_3dlut_size, - MPC_RMCM_3DLUT_MODE, &s->rmcm_regs.rmcm_3dlut_mode, - MPC_RMCM_3DLUT_MODE_CURRENT, &s->rmcm_regs.rmcm_3dlut_mode_cur); - - REG_GET_4(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[mpcc_inst], MPC_RMCM_3DLUT_READ_SEL, &s->rmcm_regs.rmcm_3dlut_read_sel, - MPC_RMCM_3DLUT_30BIT_EN, &s->rmcm_regs.rmcm_3dlut_30bit_en, - MPC_RMCM_3DLUT_WRITE_EN_MASK, &s->rmcm_regs.rmcm_3dlut_wr_en_mask, - MPC_RMCM_3DLUT_RAM_SEL, &s->rmcm_regs.rmcm_3dlut_ram_sel); - - REG_GET(MPC_RMCM_3DLUT_OUT_NORM_FACTOR[mpcc_inst], MPC_RMCM_3DLUT_OUT_NORM_FACTOR, &s->rmcm_regs.rmcm_3dlut_out_norm_factor); - - REG_GET(MPC_RMCM_3DLUT_FAST_LOAD_SELECT[mpcc_inst], MPC_RMCM_3DLUT_FL_SEL, &s->rmcm_regs.rmcm_3dlut_fl_sel); - - REG_GET_2(MPC_RMCM_3DLUT_OUT_OFFSET_R[mpcc_inst], MPC_RMCM_3DLUT_OUT_OFFSET_R, &s->rmcm_regs.rmcm_3dlut_out_offset_r, - MPC_RMCM_3DLUT_OUT_SCALE_R, &s->rmcm_regs.rmcm_3dlut_out_scale_r); - - REG_GET_3(MPC_RMCM_3DLUT_FAST_LOAD_STATUS[mpcc_inst], MPC_RMCM_3DLUT_FL_DONE, &s->rmcm_regs.rmcm_3dlut_fl_done, - MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, &s->rmcm_regs.rmcm_3dlut_fl_soft_underflow, - MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, &s->rmcm_regs.rmcm_3dlut_fl_hard_underflow); - - /* RMCM Shaper Status */ - REG_GET_4(MPC_RMCM_MEM_PWR_CTRL[mpcc_inst], MPC_RMCM_SHAPER_MEM_PWR_FORCE, &s->rmcm_regs.rmcm_shaper_mem_pwr_force, - MPC_RMCM_SHAPER_MEM_PWR_DIS, &s->rmcm_regs.rmcm_shaper_mem_pwr_dis, - MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, &s->rmcm_regs.rmcm_shaper_mem_pwr_mode, - MPC_RMCM_SHAPER_MEM_PWR_STATE, &s->rmcm_regs.rmcm_shaper_mem_pwr_state); - - REG_GET_2(MPC_RMCM_SHAPER_CONTROL[mpcc_inst], MPC_RMCM_SHAPER_LUT_MODE, &s->rmcm_regs.rmcm_shaper_lut_mode, - MPC_RMCM_SHAPER_MODE_CURRENT, &s->rmcm_regs.rmcm_shaper_mode_cur); - - REG_GET_2(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_inst], MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, &s->rmcm_regs.rmcm_shaper_lut_write_en_mask, - MPC_RMCM_SHAPER_LUT_WRITE_SEL, &s->rmcm_regs.rmcm_shaper_lut_write_sel); - - REG_GET(MPC_RMCM_SHAPER_OFFSET_B[mpcc_inst], MPC_RMCM_SHAPER_OFFSET_B, &s->rmcm_regs.rmcm_shaper_offset_b); - - REG_GET(MPC_RMCM_SHAPER_SCALE_G_B[mpcc_inst], MPC_RMCM_SHAPER_SCALE_B, &s->rmcm_regs.rmcm_shaper_scale_b); - - REG_GET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_B[mpcc_inst], MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, &s->rmcm_regs.rmcm_shaper_rama_exp_region_start_b, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, &s->rmcm_regs.rmcm_shaper_rama_exp_region_start_seg_b); - - REG_GET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_B[mpcc_inst], MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, &s->rmcm_regs.rmcm_shaper_rama_exp_region_end_b, - MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, &s->rmcm_regs.rmcm_shaper_rama_exp_region_end_base_b); - - REG_GET(MPC_RMCM_CNTL[mpcc_inst], MPC_RMCM_CNTL, &s->rmcm_regs.rmcm_cntl); - } } static const struct mpc_funcs dcn42_mpc_funcs = { @@ -774,19 +108,6 @@ static const struct mpc_funcs dcn42_mpc_funcs = { .program_lut_read_write_control = mpc401_program_lut_read_write_control, .program_lut_mode = mpc401_program_lut_mode, .get_lut_mode = mpc401_get_lut_mode, - .rmcm = { - .enable_3dlut_fl = mpc42_enable_3dlut_fl, - .update_3dlut_fast_load_select = mpc42_update_3dlut_fast_load_select, - .program_lut_read_write_control = mpc42_program_rmcm_lut_read_write_control, - .program_lut_mode = mpc42_program_lut_mode, - .program_3dlut_size = mpc42_program_rmcm_3dlut_size, - .program_bias_scale = mpc42_program_rmcm_3dlut_fast_load_bias_scale, - .program_bit_depth = mpc42_program_rmcm_bit_depth, - .power_on_shaper_3dlut = mpc42_power_on_rmcm_shaper_3dlut, - .populate_lut = mpc42_populate_rmcm_lut, - .fl_3dlut_configure = mpc42_set_fl_config, - .get_3dlut_mode = mpc42_get_rmcm_3dlut_mode, - }, }; void dcn42_mpc_construct(struct dcn42_mpc *mpc42, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h index af3b90af5396..213380832087 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.h @@ -196,327 +196,7 @@ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_G, MPCC_MCM_3DLUT_OUT_OFFSET_G, mask_sh),\ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_G, MPCC_MCM_3DLUT_OUT_SCALE_G, mask_sh),\ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_OFFSET_B, mask_sh),\ - SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_LUT_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM_SHAPER_OFFSET_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM_SHAPER_OFFSET_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM_SHAPER_OFFSET_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM_SHAPER_SCALE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM_SHAPER_LUT_INDEX, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM_SHAPER_LUT_DATA, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_SIZE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_INDEX, MPC_RMCM_3DLUT_INDEX, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA0, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA1, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM_3DLUT_DATA_30BIT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_WRITE_EN_MASK, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_RAM_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_30BIT_EN, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_READ_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM_3DLUT_OUT_NORM_FACTOR, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_OFFSET_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_SCALE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_OFFSET_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_SCALE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_OFFSET_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C11_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C12_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C13_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C14_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C21_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C22_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C23_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C24_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C31_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C32_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C33_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C34_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C11_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C12_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C13_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C14_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C21_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C22_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C23_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C24_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C31_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C32_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C33_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C34_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_FORCE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_DIS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_FORCE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_DIS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_STATE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_STATE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM_3DLUT_FL_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_DONE, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_CNTL, MPC_RMCM_CNTL, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_INDEX, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_WRITE_EN, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM_TEST_DEBUG_DATA, mask_sh) - -#define MPC_RMCM_REG_LIST_DCN42(inst)\ - SRII(MPC_RMCM_SHAPER_CONTROL, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM, inst),\ - SRII(MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_MODE, MPC_RMCM, inst), /*TODO: may need to add other 3DLUT regs*/\ - SRII(MPC_RMCM_3DLUT_INDEX, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_DATA, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM, inst),\ - SRII(MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM, inst),\ - SRII(MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM, inst),\ - SRII(MPC_RMCM_CNTL, MPC_RMCM, inst),\ - SRII(MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM, inst),\ - SRII(MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM, inst) - + SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_SCALE_B, mask_sh) #define MPC_REG_LIST_DCN42(inst) \ MPC_REG_LIST_DCN4_01_RI(inst),\ @@ -529,328 +209,11 @@ type MPCC_MCM_3DLUT_OUT_OFFSET_G;\ type MPCC_MCM_3DLUT_OUT_SCALE_G;\ type MPCC_MCM_3DLUT_OUT_OFFSET_B;\ - type MPCC_MCM_3DLUT_OUT_SCALE_B;\ - type MPC_RMCM_SHAPER_LUT_MODE;\ - type MPC_RMCM_SHAPER_MODE_CURRENT;\ - type MPC_RMCM_SHAPER_OFFSET_R;\ - type MPC_RMCM_SHAPER_OFFSET_G;\ - type MPC_RMCM_SHAPER_OFFSET_B;\ - type MPC_RMCM_SHAPER_SCALE_R;\ - type MPC_RMCM_SHAPER_SCALE_G;\ - type MPC_RMCM_SHAPER_SCALE_B;\ - type MPC_RMCM_SHAPER_LUT_INDEX;\ - type MPC_RMCM_SHAPER_LUT_DATA;\ - type MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK;\ - type MPC_RMCM_SHAPER_LUT_WRITE_SEL; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS; \ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET;\ - type MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS;\ - type MPC_RMCM_3DLUT_MODE;\ - type MPC_RMCM_3DLUT_SIZE;\ - type MPC_RMCM_3DLUT_MODE_CURRENT;\ - type MPC_RMCM_3DLUT_INDEX; \ - type MPC_RMCM_3DLUT_DATA0; \ - type MPC_RMCM_3DLUT_DATA1; \ - type MPC_RMCM_3DLUT_DATA_30BIT; \ - type MPC_RMCM_3DLUT_WRITE_EN_MASK;\ - type MPC_RMCM_3DLUT_RAM_SEL;\ - type MPC_RMCM_3DLUT_30BIT_EN;\ - type MPC_RMCM_3DLUT_READ_SEL;\ - type MPC_RMCM_3DLUT_OUT_NORM_FACTOR;\ - type MPC_RMCM_3DLUT_OUT_OFFSET_R;\ - type MPC_RMCM_3DLUT_OUT_SCALE_R; \ - type MPC_RMCM_3DLUT_OUT_OFFSET_G; \ - type MPC_RMCM_3DLUT_OUT_SCALE_G; \ - type MPC_RMCM_3DLUT_OUT_OFFSET_B; \ - type MPC_RMCM_3DLUT_OUT_SCALE_B;\ - type MPC_RMCM_GAMUT_REMAP_COEF_FORMAT;\ - type MPC_RMCM_GAMUT_REMAP_MODE;\ - type MPC_RMCM_GAMUT_REMAP_MODE_CURRENT;\ - type MPC_RMCM_GAMUT_REMAP_C11_A;\ - type MPC_RMCM_GAMUT_REMAP_C12_A;\ - type MPC_RMCM_GAMUT_REMAP_C13_A; \ - type MPC_RMCM_GAMUT_REMAP_C14_A; \ - type MPC_RMCM_GAMUT_REMAP_C21_A; \ - type MPC_RMCM_GAMUT_REMAP_C22_A; \ - type MPC_RMCM_GAMUT_REMAP_C23_A;\ - type MPC_RMCM_GAMUT_REMAP_C24_A;\ - type MPC_RMCM_GAMUT_REMAP_C31_A;\ - type MPC_RMCM_GAMUT_REMAP_C32_A;\ - type MPC_RMCM_GAMUT_REMAP_C33_A;\ - type MPC_RMCM_GAMUT_REMAP_C34_A;\ - type MPC_RMCM_GAMUT_REMAP_C11_B;\ - type MPC_RMCM_GAMUT_REMAP_C12_B;\ - type MPC_RMCM_GAMUT_REMAP_C13_B; \ - type MPC_RMCM_GAMUT_REMAP_C14_B; \ - type MPC_RMCM_GAMUT_REMAP_C21_B; \ - type MPC_RMCM_GAMUT_REMAP_C22_B; \ - type MPC_RMCM_GAMUT_REMAP_C23_B;\ - type MPC_RMCM_GAMUT_REMAP_C24_B;\ - type MPC_RMCM_GAMUT_REMAP_C31_B;\ - type MPC_RMCM_GAMUT_REMAP_C32_B;\ - type MPC_RMCM_GAMUT_REMAP_C33_B;\ - type MPC_RMCM_GAMUT_REMAP_C34_B;\ - type MPC_RMCM_SHAPER_MEM_PWR_FORCE; \ - type MPC_RMCM_SHAPER_MEM_PWR_DIS; \ - type MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE; \ - type MPC_RMCM_3DLUT_MEM_PWR_FORCE;\ - type MPC_RMCM_3DLUT_MEM_PWR_DIS;\ - type MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE;\ - type MPC_RMCM_SHAPER_MEM_PWR_STATE;\ - type MPC_RMCM_3DLUT_MEM_PWR_STATE;\ - type MPC_RMCM_3DLUT_FL_SEL;\ - type MPC_RMCM_3DLUT_FL_DONE; \ - type MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW; \ - type MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW; \ - type MPC_RMCM_CNTL; \ - type MPC_RMCM_TEST_DEBUG_INDEX;\ - type MPC_RMCM_TEST_DEBUG_WRITE_EN;\ - type MPC_RMCM_TEST_DEBUG_DATA + type MPCC_MCM_3DLUT_OUT_SCALE_B #define MPC_REG_VARIABLE_LIST_DCN42 \ MPC_REG_VARIABLE_LIST_DCN4_01 \ - uint32_t MPCC_CONTROL2[MAX_MPCC];\ - uint32_t MPC_RMCM_SHAPER_CONTROL[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_OFFSET_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_OFFSET_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_OFFSET_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_SCALE_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_SCALE_G_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_LUT_INDEX[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_LUT_DATA[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_0_1[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_2_3[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_4_5[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_6_7[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_8_9[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_10_11[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_12_13[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_14_15[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_16_17[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_18_19[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_20_21[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_22_23[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_24_25[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_26_27[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_28_29[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_30_31[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMA_REGION_32_33[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_0_1[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_2_3[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_4_5[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_6_7[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_8_9[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_10_11[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_12_13[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_14_15[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_16_17[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_18_19[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_20_21[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_22_23[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_24_25[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_26_27[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_28_29[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_30_31[MAX_MPCC]; \ - uint32_t MPC_RMCM_SHAPER_RAMB_REGION_32_33[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_MODE[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_INDEX[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_DATA[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_DATA_30BIT[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_READ_WRITE_CONTROL[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_OUT_NORM_FACTOR[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_R[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_G[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_COEF_FORMAT[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_MODE[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C11_C12_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C13_C14_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C21_C22_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C23_C24_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C31_C32_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C33_C34_A[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C11_C12_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C13_C14_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C21_C22_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C23_C24_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C31_C32_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_GAMUT_REMAP_C33_C34_B[MAX_MPCC]; \ - uint32_t MPC_RMCM_MEM_PWR_CTRL[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_FAST_LOAD_SELECT[MAX_MPCC]; \ - uint32_t MPC_RMCM_3DLUT_FAST_LOAD_STATUS[MAX_MPCC]; \ - uint32_t MPC_RMCM_CNTL[MAX_MPCC]; \ - uint32_t MPC_RMCM_TEST_DEBUG_INDEX[MAX_MPCC]; \ - uint32_t MPC_RMCM_TEST_DEBUG_DATA[MAX_MPCC] + uint32_t MPCC_CONTROL2[MAX_MPCC] struct dcn42_mpc_shift { MPC_REG_FIELD_LIST_DCN42(uint8_t); @@ -884,77 +247,6 @@ void dcn42_mpc_construct(struct dcn42_mpc *mpc401, void mpc42_init_mpcc(struct mpcc *mpcc, int mpcc_inst); -/* RMCM */ -void mpc42_program_rmcm_shaper_lut( - struct mpc *mpc, - const struct pwl_result_data *rgb, - uint32_t num, - uint32_t mpcc_id); -void mpc42_program_rmcm_shaper_lutb_settings( - struct mpc *mpc, - const struct pwl_params *params, - uint32_t mpcc_id); -void mpc42_program_rmcm_shaper_luta_settings( - struct mpc *mpc, - const struct pwl_params *params, - uint32_t mpcc_id); -void mpc42_configure_rmcm_shaper_lut( - struct mpc *mpc, - bool is_ram_a, - uint32_t mpcc_id); -void mpc42_power_on_rmcm_shaper_3dlut( - struct mpc *mpc, - uint32_t mpcc_id, - bool power_on); -void mpc42_enable_3dlut_fl( - struct mpc *mpc, - bool enable, - int mpcc_id); -void mpc42_update_3dlut_fast_load_select( - struct mpc *mpc, - int mpcc_id, - int hubp_idx); -void mpc42_populate_rmcm_lut( - struct mpc *mpc, - const union mcm_lut_params params, - bool lut_bank_a, - int mpcc_id); -void mpc42_program_rmcm_lut_read_write_control( - struct mpc *mpc, - const enum MCM_LUT_ID id, - bool lut_bank_a, - bool enabled, - int mpcc_id); -void mpc42_program_lut_mode( - struct mpc *mpc, - bool enable, - bool lut_bank_a, - int mpcc_id); -void mpc42_program_rmcm_3dlut_size( - struct mpc *mpc, - const enum dc_cm_lut_size size, - int mpcc_id); -void mpc42_program_rmcm_3dlut_fast_load_bias_scale( - struct mpc *mpc, - uint16_t bias, - uint16_t scale, - int mpcc_id); -void mpc42_program_rmcm_bit_depth( - struct mpc *mpc, - uint16_t bit_depth, - int mpcc_id); - -void mpc42_set_fl_config( - struct mpc *mpc, - struct mpc_fl_3dlut_config *cfg, - int mpcc_id); - -void mpc42_get_rmcm_3dlut_mode( - struct mpc *mpc, - int mpcc_id, - bool *enable, - bool *lut_bank_a); - void mpc42_read_mpcc_state( struct mpc *mpc, int mpcc_inst, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c index 2c0b92854a9c..ae6f2f1b7eca 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c @@ -111,34 +111,6 @@ static struct mpcc *mpc60_insert_plane( return new_mpcc; } -void mpc60_program_rmcm_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, - bool lut_bank_a, bool enabled, int mpcc_id) -{ - struct dcn60_mpc *mpc60 = TO_DCN60_MPC(mpc); - - switch (id) { - case MCM_LUT_3DLUT: - REG_UPDATE(MPC_RMCM_3DLUT_MODE[mpcc_id], MPC_RMCM_3DLUT_MODE, - (!enabled) ? 0 : - (lut_bank_a) ? 1 : 2); - break; - - case MCM_LUT_SHAPER: - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); - - REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[mpcc_id], - MPC_RMCM_SHAPER_LUT_WRITE_SEL, - lut_bank_a == true ? 0 : 1); - - REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[mpcc_id], 0, - MPC_RMCM_SHAPER_LUT_INDEX, 0); - break; - default: - break; - } -} - static void mpc60_program_lut_read_write_control(struct mpc *mpc, const enum MCM_LUT_ID id, const bool lut_bank_a, @@ -288,18 +260,6 @@ static const struct mpc_funcs dcn60_mpc_funcs = { .program_lut_read_write_control = mpc60_program_lut_read_write_control, .program_lut_mode = mpc60_program_lut_mode, .get_lut_mode = mpc401_get_lut_mode, - .rmcm = { - .enable_3dlut_fl = mpc42_enable_3dlut_fl, - .update_3dlut_fast_load_select = mpc42_update_3dlut_fast_load_select, - .program_lut_read_write_control = mpc60_program_rmcm_lut_read_write_control, - .program_lut_mode = mpc42_program_lut_mode, - .program_3dlut_size = mpc42_program_rmcm_3dlut_size, - .program_bias_scale = mpc42_program_rmcm_3dlut_fast_load_bias_scale, - .program_bit_depth = mpc42_program_rmcm_bit_depth, - .power_on_shaper_3dlut = mpc42_power_on_rmcm_shaper_3dlut, - .populate_lut = mpc42_populate_rmcm_lut, - .get_3dlut_mode = mpc42_get_rmcm_3dlut_mode, - }, }; void dcn60_mpc_construct(struct dcn60_mpc *mpc60, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h index 640a322d15c1..44ee84d74104 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.h @@ -180,238 +180,6 @@ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_G, MPCC_MCM_3DLUT_OUT_SCALE_G, mask_sh),\ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_OFFSET_B, mask_sh),\ SF(MPCC_MCM0_MPCC_MCM_3DLUT_OUT_OFFSET_B, MPCC_MCM_3DLUT_OUT_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_LUT_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM_SHAPER_OFFSET_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM_SHAPER_OFFSET_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM_SHAPER_OFFSET_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM_SHAPER_SCALE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM_SHAPER_LUT_INDEX, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM_SHAPER_LUT_DATA, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_SIZE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_INDEX, MPC_RMCM_3DLUT_INDEX, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA0, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA1, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM_3DLUT_DATA_30BIT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_WRITE_EN_MASK, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_30BIT_EN, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_READ_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM_3DLUT_OUT_NORM_FACTOR, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_OFFSET_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_SCALE_R, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_OFFSET_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_SCALE_G, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_OFFSET_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_SCALE_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE_CURRENT, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C11_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C12_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C13_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C14_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C21_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C22_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C23_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C24_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C31_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C32_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C33_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C34_A, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C11_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C12_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C13_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C14_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C21_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C22_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C23_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C24_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C31_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C32_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C33_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C34_B, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_FORCE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_DIS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_FORCE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_DIS, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_STATE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_STATE, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM_3DLUT_FL_SEL, mask_sh),\ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_DONE, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_CNTL, MPC_RMCM_CNTL, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_INDEX, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_WRITE_EN, mask_sh), \ - SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM_TEST_DEBUG_DATA, mask_sh), \ SF(MPC_OUT0_CSC_MODE, MPC_OCSC_MODE, mask_sh),\ SF(MPC_OUT0_CSC_C11_C12_A, MPC_OCSC_C11_A, mask_sh),\ SF(MPC_OUT0_CSC_C11_C12_A, MPC_OCSC_C12_A, mask_sh),\ @@ -463,10 +231,4 @@ void dcn60_mpc_construct(struct dcn60_mpc *mpc401, const struct dcn60_mpc_mask *mpc_mask, int num_mpcc, int num_rmu); - -void mpc60_program_rmcm_lut_read_write_control(struct mpc *mpc, - const enum MCM_LUT_ID id, - bool lut_bank_a, - bool enabled, - int mpcc_id); #endif /* DCN60_MPC_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 3903ec22e6af..e5e94e955157 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -27,6 +27,7 @@ #include "dcn42/dcn42_hubbub.h" #include "dcn401/dcn401_mpc.h" #include "dcn42/dcn42_mpc.h" +#include "dcn42/dcn42_rmcm.h" #include "dcn35/dcn35_hubp.h" #include "dcn42/dcn42_hubp.h" #include "irq/dcn42/irq_service_dcn42.h" @@ -436,9 +437,7 @@ static struct dcn42_mpc_registers mpc_regs; MPC_OUT_MUX_REG_LIST_DCN3_0_RI(1), \ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(2), \ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(3), \ - MPC_DWB_MUX_REG_LIST_DCN3_0_RI(0), \ - MPC_RMCM_REG_LIST_DCN42(0), \ - MPC_RMCM_REG_LIST_DCN42(1) + MPC_DWB_MUX_REG_LIST_DCN3_0_RI(0) static const struct dcn42_mpc_shift mpc_shift = { MPC_COMMON_MASK_SH_LIST_DCN42(__SHIFT)}; @@ -446,6 +445,18 @@ static const struct dcn42_mpc_shift mpc_shift = { static const struct dcn42_mpc_mask mpc_mask = { MPC_COMMON_MASK_SH_LIST_DCN42(_MASK)}; +static struct dcn42_rmcm_registers rmcm_regs; + +#define dcn_rmcm_regs_init() \ + MPC_RMCM_REG_LIST_DCN42(0), \ + MPC_RMCM_REG_LIST_DCN42(1) + +static const struct dcn42_rmcm_shift rmcm_shift = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(__SHIFT)}; + +static const struct dcn42_rmcm_mask rmcm_mask = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(_MASK)}; + #define optc_regs_init(id) \ OPTC_COMMON_REG_LIST_DCN42_RI(id) @@ -1069,6 +1080,29 @@ static struct mpc *dcn42_mpc_create( return &mpc401->base; } +static struct rmcm *dcn42_rmcm_create( + struct dc_context *ctx, + int inst) +{ + struct dcn42_rmcm *rmcm42 = kzalloc(sizeof(struct dcn42_rmcm), GFP_KERNEL); + + if (!rmcm42) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT rmcm_regs + dcn_rmcm_regs_init(); + + dcn42_rmcm_construct(rmcm42, + ctx, + &rmcm_regs, + &rmcm_shift, + &rmcm_mask, + inst); + + return &rmcm42->base; +} + static struct output_pixel_processor *dcn42_opp_create( struct dc_context *ctx, uint32_t inst) { @@ -1548,6 +1582,12 @@ static void dcn42_resource_destruct(struct dcn42_resource_pool *pool) kfree(TO_DCN20_MPC(pool->base.mpc)); pool->base.mpc = NULL; } + for (i = 0; i < MAX_RMCM_INST; i++) { + if (pool->base.rmcm[i] != NULL) { + kfree(TO_DCN42_RMCM(pool->base.rmcm[i])); + pool->base.rmcm[i] = NULL; + } + } if (pool->base.hubbub != NULL) { kfree(TO_DCN20_HUBBUB(pool->base.hubbub)); pool->base.hubbub = NULL; @@ -2330,6 +2370,16 @@ static bool dcn42_resource_construct( goto create_fail; } + /* RMCMs */ + for (i = 0; i < (unsigned int)pool->base.res_cap->num_rmcm && i < (unsigned int)MAX_RMCM_INST; i++) { + pool->base.rmcm[i] = dcn42_rmcm_create(ctx, i); + if (pool->base.rmcm[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create rmcm%d!\n", i); + goto create_fail; + } + } + /* DSCs */ for (i = 0; i < (unsigned int)pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn42_dsc_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 66ec56810209..4105150a9e12 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -31,6 +31,7 @@ #include "dcn42/dcn42_hubbub.h" #include "dcn401/dcn401_mpc.h" #include "dcn42/dcn42_mpc.h" +#include "dcn42/dcn42_rmcm.h" #include "dcn35/dcn35_hubp.h" #include "dcn42/dcn42_hubp.h" #include "irq/dcn42/irq_service_dcn42.h" @@ -486,9 +487,7 @@ static struct dcn42_mpc_registers mpc_regs; MPC_OUT_MUX_REG_LIST_DCN3_0_RI(1), \ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(2), \ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(3), \ - MPC_DWB_MUX_REG_LIST_DCN3_0_RI(0), \ - MPC_RMCM_REG_LIST_DCN42(0), \ - MPC_RMCM_REG_LIST_DCN42(1) + MPC_DWB_MUX_REG_LIST_DCN3_0_RI(0) static const struct dcn42_mpc_shift mpc_shift = { MPC_COMMON_MASK_SH_LIST_DCN42(__SHIFT)}; @@ -496,6 +495,18 @@ static const struct dcn42_mpc_shift mpc_shift = { static const struct dcn42_mpc_mask mpc_mask = { MPC_COMMON_MASK_SH_LIST_DCN42(_MASK)}; +static struct dcn42_rmcm_registers rmcm_regs; + +#define dcn_rmcm_regs_init() \ + MPC_RMCM_REG_LIST_DCN42(0), \ + MPC_RMCM_REG_LIST_DCN42(1) + +static const struct dcn42_rmcm_shift rmcm_shift = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(__SHIFT)}; + +static const struct dcn42_rmcm_mask rmcm_mask = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(_MASK)}; + #define optc_regs_init(id) \ OPTC_COMMON_REG_LIST_DCN42B_RI(id) @@ -758,6 +769,7 @@ static const struct resource_caps res_cap_dcn42b = { .num_ddc = 0, .num_vmid = 16, .num_mpc_3dlut = 2, + .num_rmcm = 2, .num_dsc = 3, .num_rmcm = 2, .num_mpc = 4, @@ -1128,6 +1140,29 @@ static struct mpc *dcn42b_mpc_create( return &mpc42b->base; } +static struct rmcm *dcn42b_rmcm_create( + struct dc_context *ctx, + int inst) +{ + struct dcn42_rmcm *rmcm42 = kzalloc(sizeof(struct dcn42_rmcm), GFP_KERNEL); + + if (!rmcm42) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT rmcm_regs + dcn_rmcm_regs_init(); + + dcn42_rmcm_construct(rmcm42, + ctx, + &rmcm_regs, + &rmcm_shift, + &rmcm_mask, + inst); + + return &rmcm42->base; +} + static struct output_pixel_processor *dcn42b_opp_create( struct dc_context *ctx, uint32_t inst) { @@ -1536,6 +1571,12 @@ static void dcn42b_resource_destruct(struct dcn42b_resource_pool *pool) kfree(TO_DCN20_MPC(pool->base.mpc)); pool->base.mpc = NULL; } + for (i = 0; i < MAX_RMCM_INST; i++) { + if (pool->base.rmcm[i] != NULL) { + kfree(TO_DCN42_RMCM(pool->base.rmcm[i])); + pool->base.rmcm[i] = NULL; + } + } if (pool->base.hubbub != NULL) { kfree(TO_DCN20_HUBBUB(pool->base.hubbub)); pool->base.hubbub = NULL; @@ -2306,6 +2347,16 @@ static bool dcn42b_resource_construct( goto create_fail; } + /* RMCMs */ + for (i = 0; i < pool->base.res_cap->num_rmcm && i < MAX_RMCM_INST; i++) { + pool->base.rmcm[i] = dcn42b_rmcm_create(ctx, i); + if (pool->base.rmcm[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create rmcm%d!\n", i); + goto create_fail; + } + } + /* DSCs */ for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn42b_dsc_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c index 2ccec5ce3b94..89d9ef5e7857 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c @@ -19,6 +19,7 @@ #include "dcn321/dcn321_resource.h" #include "dcn401/dcn401_resource.h" #include "dcn42/dcn42_resource.h" +#include "dcn60/dcn60_rmcm.h" #include "dcn60_resource.h" #include "dcn10/dcn10_ipp.h" @@ -464,9 +465,7 @@ static struct dcn60_mpc_registers mpc_regs; MPC_OUT_MUX_REG_LIST_DCN3_0_RI(0),\ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(1),\ MPC_OUT_MUX_REG_LIST_DCN3_0_RI(2),\ - MPC_OUT_MUX_REG_LIST_DCN3_0_RI(3),\ - MPC_RMCM_REG_LIST_DCN42(0),\ - MPC_RMCM_REG_LIST_DCN42(1) + MPC_OUT_MUX_REG_LIST_DCN3_0_RI(3) static const struct dcn60_mpc_shift mpc_shift = { MPC_COMMON_MASK_SH_LIST_DCN6_0(__SHIFT) @@ -1695,6 +1694,8 @@ static void dcn60_resource_destruct(struct dcn60_resource_pool *pool) kfree(TO_DCN20_MPC(pool->base.mpc)); pool->base.mpc = NULL; } + for (i = 0; i < MAX_RMCM_INST; i++) + dcn60_rmcm_destroy(&pool->base.rmcm[i]); if (pool->base.hubbub != NULL) { kfree(TO_DCN20_HUBBUB(pool->base.hubbub)); pool->base.hubbub = NULL; @@ -2288,6 +2289,16 @@ static bool dcn60_resource_construct( goto create_fail; } + /* RMCMs */ + for (i = 0; i < pool->base.res_cap->num_rmcm && i < MAX_RMCM_INST; i++) { + pool->base.rmcm[i] = dcn60_rmcm_create(ctx, i); + if (pool->base.rmcm[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create rmcm%d!\n", i); + goto create_fail; + } + } + /* DSCs */ for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn60_dsc_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/rmcm/Makefile b/drivers/gpu/drm/amd/display/dc/rmcm/Makefile new file mode 100644 index 000000000000..9dfc86a850da --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/rmcm/Makefile @@ -0,0 +1,44 @@ +# +# 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 +# +# + +ifdef CONFIG_DRM_AMD_DC_FP +############################################################################### +# DCN42 +############################################################################### +RMCM_DCN42 = dcn42_rmcm.o + +AMD_DAL_RMCM_DCN42 = $(addprefix $(AMDDALPATH)/dc/rmcm/dcn42/,$(RMCM_DCN42)) + +AMD_DISPLAY_FILES += $(AMD_DAL_RMCM_DCN42) + +############################################################################### +# DCN60 +############################################################################### +RMCM_DCN60 = dcn60_rmcm.o + +AMD_DAL_RMCM_DCN60 = $(addprefix $(AMDDALPATH)/dc/rmcm/dcn60/,$(RMCM_DCN60)) + +AMD_DISPLAY_FILES += $(AMD_DAL_RMCM_DCN60) +endif diff --git a/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.c b/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.c new file mode 100644 index 000000000000..952f0ade24a5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.c @@ -0,0 +1,867 @@ +/* + * 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 "reg_helper.h" +#include "dc.h" +#include "dcn42_rmcm.h" +#include "dcn10/dcn10_cm_common.h" +#include "basics/conversion.h" +#include "hw_shared.h" +#include "mpc.h" + +#define REG(reg)\ + rmcm42->rmcm_regs->reg + +#define CTX \ + rmcm42->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + rmcm42->rmcm_shift->field_name, rmcm42->rmcm_mask->field_name + +/* RMCM Shaper functions */ + +void rmcm42_power_on_shaper_3dlut( + struct rmcm *rmcm, + uint32_t rmcm_id, + bool power_on) +{ + uint32_t power_status_shaper = 2; + uint32_t power_status_3dlut = 2; + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + int max_retries = 10; + + REG_SET(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], 0, + MPC_RMCM_3DLUT_MEM_PWR_DIS, power_on == true ? 0 : 1); + REG_SET(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], 0, + MPC_RMCM_SHAPER_MEM_PWR_DIS, power_on == true ? 0 : 1); + /* wait for memory to fully power up */ + if (power_on && rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) { + REG_WAIT(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], MPC_RMCM_SHAPER_MEM_PWR_STATE, 0, 1, max_retries); + REG_WAIT(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], MPC_RMCM_3DLUT_MEM_PWR_STATE, 0, 1, max_retries); + } + + /*read status is not mandatory, it is just for debugging*/ + REG_GET(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], MPC_RMCM_SHAPER_MEM_PWR_STATE, &power_status_shaper); + REG_GET(MPC_RMCM_MEM_PWR_CTRL[rmcm_id], MPC_RMCM_3DLUT_MEM_PWR_STATE, &power_status_3dlut); + + if (power_status_shaper != 0 && power_on == true) + BREAK_TO_DEBUGGER(); + + if (power_status_3dlut != 0 && power_on == true) + BREAK_TO_DEBUGGER(); +} + +static void rmcm42_configure_shaper_lut( + struct rmcm *rmcm, + bool is_ram_a, + uint32_t rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_UPDATE(MPC_RMCM_SHAPER_SCALE_G_B[rmcm_id], + MPC_RMCM_SHAPER_SCALE_B, 0x7000); + REG_UPDATE(MPC_RMCM_SHAPER_SCALE_G_B[rmcm_id], + MPC_RMCM_SHAPER_SCALE_G, 0x7000); + REG_UPDATE(MPC_RMCM_SHAPER_SCALE_R[rmcm_id], + MPC_RMCM_SHAPER_SCALE_R, 0x7000); + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_SEL, is_ram_a == true ? 0:1); + REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[rmcm_id], 0, MPC_RMCM_SHAPER_LUT_INDEX, 0); +} + +static void rmcm42_program_shaper_luta_settings( + struct rmcm *rmcm, + const struct pwl_params *params, + uint32_t rmcm_id) +{ + const struct gamma_curve *curve; + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_B[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].blue.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); + REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_G[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].green.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); + REG_SET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_R[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, params->corner_points[0].red.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, 0); + + REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_B[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].blue.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].blue.custom_float_y); + REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_G[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].green.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].green.custom_float_y); + REG_SET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_R[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, params->corner_points[1].red.custom_float_x, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, params->corner_points[1].red.custom_float_y); + + curve = params->arr_curve_points; + if (curve) { + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_0_1[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_2_3[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_4_5[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_6_7[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_8_9[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_10_11[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_12_13[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_14_15[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_16_17[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_18_19[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_20_21[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_22_23[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_24_25[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_26_27[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_28_29[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_30_31[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMA_REGION_32_33[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + } +} + + +static void rmcm42_program_shaper_lutb_settings( + struct rmcm *rmcm, + const struct pwl_params *params, + uint32_t rmcm_id) +{ + const struct gamma_curve *curve; + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_B[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].blue.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); + REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_G[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].green.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); + REG_SET_2(MPC_RMCM_SHAPER_RAMB_START_CNTL_R[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, params->corner_points[0].red.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, 0); + + REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_B[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].blue.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].blue.custom_float_y); + REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_G[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].green.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].green.custom_float_y); + REG_SET_2(MPC_RMCM_SHAPER_RAMB_END_CNTL_R[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, params->corner_points[1].red.custom_float_x, + MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, params->corner_points[1].red.custom_float_y); + + curve = params->arr_curve_points; + if (curve) { + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_0_1[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_2_3[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_4_5[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_6_7[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_8_9[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_10_11[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_12_13[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_14_15[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_16_17[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_18_19[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_20_21[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_22_23[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_24_25[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_26_27[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_28_29[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_30_31[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + + curve += 2; + REG_SET_4(MPC_RMCM_SHAPER_RAMB_REGION_32_33[rmcm_id], 0, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, curve[0].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, curve[1].offset, + MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + } +} + +static void rmcm42_program_shaper_lut( + struct rmcm *rmcm, + const struct pwl_result_data *rgb, + uint32_t num, + uint32_t rmcm_id) +{ + uint32_t i, red, green, blue; + uint32_t red_delta, green_delta, blue_delta; + uint32_t red_value, green_value, blue_value; + + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + for (i = 0; i < num; i++) { + + red = rgb[i].red_reg; + green = rgb[i].green_reg; + blue = rgb[i].blue_reg; + + red_delta = rgb[i].delta_red_reg; + green_delta = rgb[i].delta_green_reg; + blue_delta = rgb[i].delta_blue_reg; + + red_value = ((red_delta & 0x3ff) << 14) | (red & 0x3fff); + green_value = ((green_delta & 0x3ff) << 14) | (green & 0x3fff); + blue_value = ((blue_delta & 0x3ff) << 14) | (blue & 0x3fff); + + REG_SET(MPC_RMCM_SHAPER_LUT_DATA[rmcm_id], 0, MPC_RMCM_SHAPER_LUT_DATA, red_value); + REG_SET(MPC_RMCM_SHAPER_LUT_DATA[rmcm_id], 0, MPC_RMCM_SHAPER_LUT_DATA, green_value); + REG_SET(MPC_RMCM_SHAPER_LUT_DATA[rmcm_id], 0, MPC_RMCM_SHAPER_LUT_DATA, blue_value); + } +} + +void rmcm42_update_3dlut_fast_load_select(struct rmcm *rmcm, int rmcm_id, int hubp_idx) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_SET(MPC_RMCM_3DLUT_FAST_LOAD_SELECT[rmcm_id], 0, + MPC_RMCM_3DLUT_FL_SEL, + hubp_idx); +} + +static void rmcm42_select_3dlut_ram_mask( + struct rmcm *rmcm, + uint32_t ram_selection_mask, + int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_WRITE_EN_MASK, ram_selection_mask); + REG_SET(MPC_RMCM_3DLUT_INDEX[rmcm_id], 0, MPC_RMCM_3DLUT_INDEX, 0); +} + +static void rmcm42_set3dlut_ram12( + struct rmcm *rmcm, + const struct dc_rgb *lut, + uint32_t entries, + int rmcm_id) +{ + uint32_t i, red, green, blue, red1, green1, blue1; + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + for (i = 0; i < entries; i += 2) { + red = lut[i].red << 4; + green = lut[i].green << 4; + blue = lut[i].blue << 4; + red1 = lut[i + 1].red << 4; + green1 = lut[i + 1].green << 4; + blue1 = lut[i + 1].blue << 4; + + REG_SET_2(MPC_RMCM_3DLUT_DATA[rmcm_id], 0, + MPC_RMCM_3DLUT_DATA0, red, + MPC_RMCM_3DLUT_DATA1, red1); + + REG_SET_2(MPC_RMCM_3DLUT_DATA[rmcm_id], 0, + MPC_RMCM_3DLUT_DATA0, green, + MPC_RMCM_3DLUT_DATA1, green1); + + REG_SET_2(MPC_RMCM_3DLUT_DATA[rmcm_id], 0, + MPC_RMCM_3DLUT_DATA0, blue, + MPC_RMCM_3DLUT_DATA1, blue1); + } +} + +static void rmcm42_set3dlut_ram10( + struct rmcm *rmcm, + const struct dc_rgb *lut, + uint32_t entries, + int rmcm_id) +{ + uint32_t i, red, green, blue, value; + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + for (i = 0; i < entries; i++) { + red = lut[i].red; + green = lut[i].green; + blue = lut[i].blue; + value = (red << 20) | (green << 10) | blue; + + REG_SET(MPC_RMCM_3DLUT_DATA_30BIT[rmcm_id], 0, + MPC_RMCM_3DLUT_DATA_30BIT, value); + } +} + +void rmcm42_populate_lut(struct rmcm *rmcm, const enum MCM_LUT_ID id, + const union rmcm_lut_params params, bool lut_bank_a, int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + const enum dc_lut_mode next_mode = lut_bank_a ? LUT_RAM_A : LUT_RAM_B; + + switch (id) { + case MCM_LUT_SHAPER: { + const struct pwl_params *lut_shaper = params.pwl; + + if (lut_shaper == NULL) + return; + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, true); + + rmcm42_configure_shaper_lut(rmcm, next_mode == LUT_RAM_A, rmcm_id); + + if (next_mode == LUT_RAM_A) + rmcm42_program_shaper_luta_settings(rmcm, lut_shaper, rmcm_id); + else + rmcm42_program_shaper_lutb_settings(rmcm, lut_shaper, rmcm_id); + + rmcm42_program_shaper_lut( + rmcm, lut_shaper->rgb_resulted, lut_shaper->hw_points_num, rmcm_id); + + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + break; + } + case MCM_LUT_3DLUT: { + const struct tetrahedral_params *lut3d = params.lut3d; + bool is_17x17x17; + bool is_12bits_color_channel; + const struct dc_rgb *lut0; + const struct dc_rgb *lut1; + const struct dc_rgb *lut2; + const struct dc_rgb *lut3; + int lut_size0; + int lut_size; + + if (lut3d == NULL) + return; + + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, true); + + /* Program bit depth before loading data */ + is_12bits_color_channel = lut3d->use_12bits; + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_WRITE_EN_MASK, 0xF); + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_30BIT_EN, + is_12bits_color_channel ? 0 : 1); + + is_17x17x17 = !lut3d->use_tetrahedral_9; + is_12bits_color_channel = lut3d->use_12bits; + if (is_17x17x17) { + lut0 = lut3d->tetrahedral_17.lut0; + lut1 = lut3d->tetrahedral_17.lut1; + lut2 = lut3d->tetrahedral_17.lut2; + lut3 = lut3d->tetrahedral_17.lut3; + lut_size0 = sizeof(lut3d->tetrahedral_17.lut0) / + sizeof(lut3d->tetrahedral_17.lut0[0]); + lut_size = sizeof(lut3d->tetrahedral_17.lut1) / + sizeof(lut3d->tetrahedral_17.lut1[0]); + } else { + lut0 = lut3d->tetrahedral_9.lut0; + lut1 = lut3d->tetrahedral_9.lut1; + lut2 = lut3d->tetrahedral_9.lut2; + lut3 = lut3d->tetrahedral_9.lut3; + lut_size0 = sizeof(lut3d->tetrahedral_9.lut0) / + sizeof(lut3d->tetrahedral_9.lut0[0]); + lut_size = sizeof(lut3d->tetrahedral_9.lut1) / + sizeof(lut3d->tetrahedral_9.lut1[0]); + } + + rmcm42_select_3dlut_ram_mask(rmcm, 0x1, rmcm_id); + if (is_12bits_color_channel) + rmcm42_set3dlut_ram12(rmcm, lut0, lut_size0, rmcm_id); + else + rmcm42_set3dlut_ram10(rmcm, lut0, lut_size0, rmcm_id); + + rmcm42_select_3dlut_ram_mask(rmcm, 0x2, rmcm_id); + if (is_12bits_color_channel) + rmcm42_set3dlut_ram12(rmcm, lut1, lut_size, rmcm_id); + else + rmcm42_set3dlut_ram10(rmcm, lut1, lut_size, rmcm_id); + + rmcm42_select_3dlut_ram_mask(rmcm, 0x4, rmcm_id); + if (is_12bits_color_channel) + rmcm42_set3dlut_ram12(rmcm, lut2, lut_size, rmcm_id); + else + rmcm42_set3dlut_ram10(rmcm, lut2, lut_size, rmcm_id); + + rmcm42_select_3dlut_ram_mask(rmcm, 0x8, rmcm_id); + if (is_12bits_color_channel) + rmcm42_set3dlut_ram12(rmcm, lut3, lut_size, rmcm_id); + else + rmcm42_set3dlut_ram10(rmcm, lut3, lut_size, rmcm_id); + + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + + break; + } + case MCM_LUT_1DLUT: + /* RMCM doesn't support 1DLUT */ + break; + } +} + +static void rmcm42_program_lut_read_write_control(struct rmcm *rmcm, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + switch (id) { + case MCM_LUT_3DLUT: + REG_UPDATE(MPC_RMCM_3DLUT_MODE[rmcm_id], MPC_RMCM_3DLUT_MODE, + (!enabled) ? 0 : + (lut_bank_a) ? 1 : 2); + + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_RAM_SEL, + (lut_bank_a) ? 0 : 1); + break; + case MCM_LUT_SHAPER: + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); + + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_SEL, + lut_bank_a == true ? 0:1); + + REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[rmcm_id], 0, + MPC_RMCM_SHAPER_LUT_INDEX, 0); + break; + default: + break; + } +} + +uint32_t rmcm42_get_3dlut_width( + const enum dc_cm_lut_size size) +{ + uint32_t width = 0; + + switch (size) { + case CM_LUT_SIZE_999: + width = 1; + break; + case CM_LUT_SIZE_333333: + width = 2; + break; + case CM_LUT_SIZE_171717: + default: + width = 0; + break; + } + + return width; +} + +static void rmcm42_program_lut_mode(struct rmcm *rmcm, + const enum MCM_LUT_ID id, + bool enable, + bool lut_bank_a, + const enum dc_cm_lut_size size, + uint16_t bias, + uint16_t scale, + int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + (void)bias; + (void)scale; + + switch (id) { + case MCM_LUT_3DLUT: + if (enable) { + uint32_t lut_size = rmcm42_get_3dlut_width(size); + + REG_UPDATE_2(MPC_RMCM_3DLUT_MODE[rmcm_id], + MPC_RMCM_3DLUT_MODE, lut_bank_a ? 1 : 2, + MPC_RMCM_3DLUT_SIZE, lut_size); + + /* Default to 12-bit (30BIT_EN=0) for DMA path; + * populate_lut overrides for host-load path. + */ + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_WRITE_EN_MASK, 0xF); + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_30BIT_EN, 0); + } else { + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + REG_UPDATE(MPC_RMCM_3DLUT_MODE[rmcm_id], MPC_RMCM_3DLUT_MODE, 0); + } + break; + case MCM_LUT_SHAPER: + if (enable) { + REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_LUT_MODE, lut_bank_a ? 1 : 2); + } else { + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_LUT_MODE, 0); + } + break; + case MCM_LUT_1DLUT: + /* RMCM doesn't support 1DLUT */ + break; + } +} + +static void rmcm42_get_lut_mode(struct rmcm *rmcm, + const enum MCM_LUT_ID id, + int rmcm_id, + bool *enable, + bool *lut_bank_a) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + uint32_t lut_mode = 0; + + *enable = false; + *lut_bank_a = true; + + switch (id) { + case MCM_LUT_SHAPER: + REG_GET(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_MODE_CURRENT, &lut_mode); + *enable = lut_mode != 0; + *lut_bank_a = lut_mode != 2; + break; + case MCM_LUT_1DLUT: + /* RMCM doesn't support 1DLUT, return disabled */ + *enable = false; + *lut_bank_a = true; + break; + case MCM_LUT_3DLUT: + default: + REG_GET(MPC_RMCM_3DLUT_MODE[rmcm_id], + MPC_RMCM_3DLUT_MODE_CURRENT, &lut_mode); + *enable = lut_mode != 0; + *lut_bank_a = lut_mode != 2; + break; + } +} + +void rmcm42_get_3dlut_fast_load_status(struct rmcm *rmcm, int rmcm_id, + bool *done, bool *soft_underflow, bool *hard_underflow) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + uint32_t fl_done = 0, fl_soft = 0, fl_hard = 0; + + REG_GET_3(MPC_RMCM_3DLUT_FAST_LOAD_STATUS[rmcm_id], + MPC_RMCM_3DLUT_FL_DONE, &fl_done, + MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, &fl_soft, + MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, &fl_hard); + + *done = (fl_done != 0); + *soft_underflow = (fl_soft != 0); + *hard_underflow = (fl_hard != 0); +} + +void rmcm42_read_state(struct rmcm *rmcm, int rmcm_inst, struct rmcm_state *s) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + /* RMCM 3DLUT Status */ + REG_GET_4(MPC_RMCM_MEM_PWR_CTRL[rmcm_inst], MPC_RMCM_3DLUT_MEM_PWR_FORCE, &s->regs.rmcm_3dlut_mem_pwr_force, + MPC_RMCM_3DLUT_MEM_PWR_DIS, &s->regs.rmcm_3dlut_mem_pwr_dis, + MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, &s->regs.rmcm_3dlut_mem_pwr_mode, + MPC_RMCM_3DLUT_MEM_PWR_STATE, &s->regs.rmcm_3dlut_mem_pwr_state); + + REG_GET_3(MPC_RMCM_3DLUT_MODE[rmcm_inst], MPC_RMCM_3DLUT_SIZE, &s->regs.rmcm_3dlut_size, + MPC_RMCM_3DLUT_MODE, &s->regs.rmcm_3dlut_mode, + MPC_RMCM_3DLUT_MODE_CURRENT, &s->regs.rmcm_3dlut_mode_cur); + + REG_GET_4(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_inst], MPC_RMCM_3DLUT_READ_SEL, &s->regs.rmcm_3dlut_read_sel, + MPC_RMCM_3DLUT_30BIT_EN, &s->regs.rmcm_3dlut_30bit_en, + MPC_RMCM_3DLUT_WRITE_EN_MASK, &s->regs.rmcm_3dlut_wr_en_mask, + MPC_RMCM_3DLUT_RAM_SEL, &s->regs.rmcm_3dlut_ram_sel); + + REG_GET(MPC_RMCM_3DLUT_OUT_NORM_FACTOR[rmcm_inst], MPC_RMCM_3DLUT_OUT_NORM_FACTOR, &s->regs.rmcm_3dlut_out_norm_factor); + + REG_GET(MPC_RMCM_3DLUT_FAST_LOAD_SELECT[rmcm_inst], MPC_RMCM_3DLUT_FL_SEL, &s->regs.rmcm_3dlut_fl_sel); + + REG_GET_2(MPC_RMCM_3DLUT_OUT_OFFSET_R[rmcm_inst], MPC_RMCM_3DLUT_OUT_OFFSET_R, &s->regs.rmcm_3dlut_out_offset_r, + MPC_RMCM_3DLUT_OUT_SCALE_R, &s->regs.rmcm_3dlut_out_scale_r); + + REG_GET_3(MPC_RMCM_3DLUT_FAST_LOAD_STATUS[rmcm_inst], MPC_RMCM_3DLUT_FL_DONE, &s->regs.rmcm_3dlut_fl_done, + MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, &s->regs.rmcm_3dlut_fl_soft_underflow, + MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, &s->regs.rmcm_3dlut_fl_hard_underflow); + + /* RMCM Shaper Status */ + REG_GET_4(MPC_RMCM_MEM_PWR_CTRL[rmcm_inst], MPC_RMCM_SHAPER_MEM_PWR_FORCE, &s->regs.rmcm_shaper_mem_pwr_force, + MPC_RMCM_SHAPER_MEM_PWR_DIS, &s->regs.rmcm_shaper_mem_pwr_dis, + MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, &s->regs.rmcm_shaper_mem_pwr_mode, + MPC_RMCM_SHAPER_MEM_PWR_STATE, &s->regs.rmcm_shaper_mem_pwr_state); + + REG_GET_2(MPC_RMCM_SHAPER_CONTROL[rmcm_inst], MPC_RMCM_SHAPER_LUT_MODE, &s->regs.rmcm_shaper_lut_mode, + MPC_RMCM_SHAPER_MODE_CURRENT, &s->regs.rmcm_shaper_mode_cur); + + REG_GET_2(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_inst], MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, &s->regs.rmcm_shaper_lut_write_en_mask, + MPC_RMCM_SHAPER_LUT_WRITE_SEL, &s->regs.rmcm_shaper_lut_write_sel); + + REG_GET(MPC_RMCM_SHAPER_OFFSET_B[rmcm_inst], MPC_RMCM_SHAPER_OFFSET_B, &s->regs.rmcm_shaper_offset_b); + + REG_GET(MPC_RMCM_SHAPER_SCALE_G_B[rmcm_inst], MPC_RMCM_SHAPER_SCALE_B, &s->regs.rmcm_shaper_scale_b); + + REG_GET_2(MPC_RMCM_SHAPER_RAMA_START_CNTL_B[rmcm_inst], MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, &s->regs.rmcm_shaper_rama_exp_region_start_b, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, &s->regs.rmcm_shaper_rama_exp_region_start_seg_b); + + REG_GET_2(MPC_RMCM_SHAPER_RAMA_END_CNTL_B[rmcm_inst], MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, &s->regs.rmcm_shaper_rama_exp_region_end_b, + MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, &s->regs.rmcm_shaper_rama_exp_region_end_base_b); + + REG_GET(MPC_RMCM_CNTL[rmcm_inst], MPC_RMCM_CNTL, &s->regs.rmcm_cntl); +} + +/* + * rmcm42_connect_mpcc() - Program RMCM to MPCC routing mux + * @rmcm: RMCM instance + * @rmcm_id: RMCM hardware index + * @mpcc_id: MPCC to connect (0-3), or RMCM_MPCC_ID_NONE to disconnect + * + * Programs MPC_RMCM_CNTL to route the specified MPCC to this RMCM instance, + * allowing any MPCC[x] to RMCM[y] mapping. + */ +void rmcm42_connect_mpcc(struct rmcm *rmcm, int rmcm_id, int mpcc_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + REG_UPDATE(MPC_RMCM_CNTL[rmcm_id], MPC_RMCM_CNTL, mpcc_id); + + rmcm->mpcc_id = mpcc_id; +} + +const struct rmcm_funcs dcn42_rmcm_funcs = { + .get_lut_mode = rmcm42_get_lut_mode, + .program_lut_mode = rmcm42_program_lut_mode, + .populate_lut = rmcm42_populate_lut, + .program_lut_read_write_control = rmcm42_program_lut_read_write_control, + .update_3dlut_fast_load_select = rmcm42_update_3dlut_fast_load_select, + .get_3dlut_fast_load_status = rmcm42_get_3dlut_fast_load_status, + .read_rmcm_state = rmcm42_read_state, + .connect_mpcc = rmcm42_connect_mpcc, +}; + +void dcn42_rmcm_construct( + struct dcn42_rmcm *rmcm42, + struct dc_context *ctx, + const struct dcn42_rmcm_registers *regs, + const struct dcn42_rmcm_shift *shift, + const struct dcn42_rmcm_mask *mask, + int inst) +{ + rmcm42->base.ctx = ctx; + rmcm42->base.inst = inst; + rmcm42->base.mpcc_id = RMCM_MPCC_ID_UNKNOWN; + rmcm42->base.funcs = &dcn42_rmcm_funcs; + rmcm42->rmcm_regs = regs; + rmcm42->rmcm_shift = shift; + rmcm42->rmcm_mask = mask; +} diff --git a/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.h b/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.h new file mode 100644 index 000000000000..caffc14fab05 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/rmcm/dcn42/dcn42_rmcm.h @@ -0,0 +1,717 @@ +/* + * 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 + * + */ + +#ifndef __DC_RMCM_DCN42_H__ +#define __DC_RMCM_DCN42_H__ + +#include "rmcm.h" + +#define TO_DCN42_RMCM(rmcm_base) \ + container_of(rmcm_base, struct dcn42_rmcm, base) + +#define MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(mask_sh) \ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_LUT_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_CONTROL, MPC_RMCM_SHAPER_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM_SHAPER_OFFSET_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM_SHAPER_OFFSET_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM_SHAPER_OFFSET_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM_SHAPER_SCALE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM_SHAPER_SCALE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM_SHAPER_LUT_INDEX, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM_SHAPER_LUT_DATA, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM_SHAPER_LUT_WRITE_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_SIZE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_MODE, MPC_RMCM_3DLUT_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_INDEX, MPC_RMCM_3DLUT_INDEX, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA0, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA, MPC_RMCM_3DLUT_DATA1, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM_3DLUT_DATA_30BIT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_WRITE_EN_MASK, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_RAM_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_30BIT_EN, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM_3DLUT_READ_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM_3DLUT_OUT_NORM_FACTOR, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_OFFSET_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM_3DLUT_OUT_SCALE_R, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_OFFSET_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM_3DLUT_OUT_SCALE_G, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_OFFSET_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM_3DLUT_OUT_SCALE_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM_GAMUT_REMAP_MODE_CURRENT, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C11_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM_GAMUT_REMAP_C12_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C13_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM_GAMUT_REMAP_C14_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C21_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM_GAMUT_REMAP_C22_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C23_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM_GAMUT_REMAP_C24_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C31_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM_GAMUT_REMAP_C32_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C33_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM_GAMUT_REMAP_C34_A, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C11_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM_GAMUT_REMAP_C12_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C13_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM_GAMUT_REMAP_C14_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C21_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM_GAMUT_REMAP_C22_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C23_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM_GAMUT_REMAP_C24_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C31_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM_GAMUT_REMAP_C32_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C33_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM_GAMUT_REMAP_C34_B, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_FORCE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_DIS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_FORCE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_DIS, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_SHAPER_MEM_PWR_STATE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM_3DLUT_MEM_PWR_STATE, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM_3DLUT_FL_SEL, mask_sh),\ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_DONE, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_CNTL, MPC_RMCM_CNTL, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_INDEX, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM_TEST_DEBUG_WRITE_EN, mask_sh), \ + SF(MPC_RMCM0_MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM_TEST_DEBUG_DATA, mask_sh) +#define MPC_RMCM_REG_LIST_DCN42(inst)\ + SRII(MPC_RMCM_SHAPER_CONTROL, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_OFFSET_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_OFFSET_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_OFFSET_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_SCALE_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_SCALE_G_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_LUT_INDEX, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_LUT_DATA, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_START_CNTL_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_END_CNTL_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_0_1, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_2_3, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_4_5, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_6_7, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_8_9, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_10_11, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_12_13, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_14_15, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_16_17, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_18_19, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_20_21, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_22_23, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_24_25, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_26_27, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_28_29, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_30_31, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMA_REGION_32_33, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_START_CNTL_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_END_CNTL_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_0_1, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_2_3, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_4_5, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_6_7, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_8_9, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_10_11, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_12_13, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_14_15, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_16_17, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_18_19, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_20_21, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_22_23, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_24_25, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_26_27, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_28_29, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_30_31, MPC_RMCM, inst),\ + SRII(MPC_RMCM_SHAPER_RAMB_REGION_32_33, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_MODE, MPC_RMCM, inst), /*TODO: may need to add other 3DLUT regs*/\ + SRII(MPC_RMCM_3DLUT_INDEX, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_DATA, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_DATA_30BIT, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_READ_WRITE_CONTROL, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_OUT_NORM_FACTOR, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_OUT_OFFSET_R, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_OUT_OFFSET_G, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_OUT_OFFSET_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_COEF_FORMAT, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_MODE, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C11_C12_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C13_C14_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C21_C22_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C23_C24_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C31_C32_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C33_C34_A, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C11_C12_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C13_C14_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C21_C22_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C23_C24_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C31_C32_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_GAMUT_REMAP_C33_C34_B, MPC_RMCM, inst),\ + SRII(MPC_RMCM_MEM_PWR_CTRL, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_FAST_LOAD_SELECT, MPC_RMCM, inst),\ + SRII(MPC_RMCM_3DLUT_FAST_LOAD_STATUS, MPC_RMCM, inst),\ + SRII(MPC_RMCM_CNTL, MPC_RMCM, inst),\ + SRII(MPC_RMCM_TEST_DEBUG_INDEX, MPC_RMCM, inst),\ + SRII(MPC_RMCM_TEST_DEBUG_DATA, MPC_RMCM, inst) + +#define MPC_RMCM_REG_FIELD_LIST_DCN42(type) \ + type MPC_RMCM_SHAPER_LUT_MODE;\ + type MPC_RMCM_SHAPER_MODE_CURRENT;\ + type MPC_RMCM_SHAPER_OFFSET_R;\ + type MPC_RMCM_SHAPER_OFFSET_G;\ + type MPC_RMCM_SHAPER_OFFSET_B;\ + type MPC_RMCM_SHAPER_SCALE_R;\ + type MPC_RMCM_SHAPER_SCALE_G;\ + type MPC_RMCM_SHAPER_SCALE_B;\ + type MPC_RMCM_SHAPER_LUT_INDEX;\ + type MPC_RMCM_SHAPER_LUT_DATA;\ + type MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK;\ + type MPC_RMCM_SHAPER_LUT_WRITE_SEL; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_B; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_B; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_G; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_G;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_R;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_START_SEGMENT_R;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_B;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_B;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_G;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_G;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_R;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION_END_BASE_R;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION0_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION0_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION1_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION1_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION2_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION2_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION3_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION3_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION4_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION4_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION5_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION5_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION6_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION6_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION7_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION7_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION8_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION8_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION9_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION9_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION10_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION10_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION11_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION11_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION12_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION12_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION13_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION13_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION14_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION14_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION15_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION15_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION16_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION16_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION17_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION17_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION18_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION18_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION19_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION19_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION20_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION20_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION21_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION21_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION22_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION22_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION23_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION23_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION24_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION24_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION25_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION25_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION26_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION26_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION27_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION27_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION28_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION28_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION29_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION29_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION30_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION30_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION31_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION31_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION32_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION32_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION33_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMA_EXP_REGION33_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_B; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_B; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_G; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_G;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_R;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_START_SEGMENT_R;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_B;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_B;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_G;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_G;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_R;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION_END_BASE_R;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION0_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION0_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION1_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION1_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION2_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION2_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION3_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION3_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION4_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION4_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION5_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION5_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION6_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION6_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION7_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION7_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION8_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION8_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION9_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION9_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION10_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION10_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION11_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION11_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION12_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION12_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION13_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION13_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION14_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION14_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION15_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION15_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION16_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION16_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION17_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION17_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION18_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION18_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION19_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION19_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION20_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION20_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION21_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION21_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION22_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION22_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION23_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION23_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION24_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION24_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION25_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION25_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION26_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION26_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION27_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION27_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION28_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION28_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION29_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION29_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION30_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION30_NUM_SEGMENTS;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION31_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION31_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION32_LUT_OFFSET; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION32_NUM_SEGMENTS; \ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION33_LUT_OFFSET;\ + type MPC_RMCM_SHAPER_RAMB_EXP_REGION33_NUM_SEGMENTS;\ + type MPC_RMCM_3DLUT_MODE;\ + type MPC_RMCM_3DLUT_SIZE;\ + type MPC_RMCM_3DLUT_MODE_CURRENT;\ + type MPC_RMCM_3DLUT_INDEX; \ + type MPC_RMCM_3DLUT_DATA0; \ + type MPC_RMCM_3DLUT_DATA1; \ + type MPC_RMCM_3DLUT_DATA_30BIT; \ + type MPC_RMCM_3DLUT_WRITE_EN_MASK;\ + type MPC_RMCM_3DLUT_RAM_SEL;\ + type MPC_RMCM_3DLUT_30BIT_EN;\ + type MPC_RMCM_3DLUT_READ_SEL;\ + type MPC_RMCM_3DLUT_OUT_NORM_FACTOR;\ + type MPC_RMCM_3DLUT_OUT_OFFSET_R;\ + type MPC_RMCM_3DLUT_OUT_SCALE_R; \ + type MPC_RMCM_3DLUT_OUT_OFFSET_G; \ + type MPC_RMCM_3DLUT_OUT_SCALE_G; \ + type MPC_RMCM_3DLUT_OUT_OFFSET_B; \ + type MPC_RMCM_3DLUT_OUT_SCALE_B;\ + type MPC_RMCM_GAMUT_REMAP_COEF_FORMAT;\ + type MPC_RMCM_GAMUT_REMAP_MODE;\ + type MPC_RMCM_GAMUT_REMAP_MODE_CURRENT;\ + type MPC_RMCM_GAMUT_REMAP_C11_A;\ + type MPC_RMCM_GAMUT_REMAP_C12_A;\ + type MPC_RMCM_GAMUT_REMAP_C13_A; \ + type MPC_RMCM_GAMUT_REMAP_C14_A; \ + type MPC_RMCM_GAMUT_REMAP_C21_A; \ + type MPC_RMCM_GAMUT_REMAP_C22_A; \ + type MPC_RMCM_GAMUT_REMAP_C23_A;\ + type MPC_RMCM_GAMUT_REMAP_C24_A;\ + type MPC_RMCM_GAMUT_REMAP_C31_A;\ + type MPC_RMCM_GAMUT_REMAP_C32_A;\ + type MPC_RMCM_GAMUT_REMAP_C33_A;\ + type MPC_RMCM_GAMUT_REMAP_C34_A;\ + type MPC_RMCM_GAMUT_REMAP_C11_B;\ + type MPC_RMCM_GAMUT_REMAP_C12_B;\ + type MPC_RMCM_GAMUT_REMAP_C13_B; \ + type MPC_RMCM_GAMUT_REMAP_C14_B; \ + type MPC_RMCM_GAMUT_REMAP_C21_B; \ + type MPC_RMCM_GAMUT_REMAP_C22_B; \ + type MPC_RMCM_GAMUT_REMAP_C23_B;\ + type MPC_RMCM_GAMUT_REMAP_C24_B;\ + type MPC_RMCM_GAMUT_REMAP_C31_B;\ + type MPC_RMCM_GAMUT_REMAP_C32_B;\ + type MPC_RMCM_GAMUT_REMAP_C33_B;\ + type MPC_RMCM_GAMUT_REMAP_C34_B;\ + type MPC_RMCM_SHAPER_MEM_PWR_FORCE; \ + type MPC_RMCM_SHAPER_MEM_PWR_DIS; \ + type MPC_RMCM_SHAPER_MEM_LOW_PWR_MODE; \ + type MPC_RMCM_3DLUT_MEM_PWR_FORCE;\ + type MPC_RMCM_3DLUT_MEM_PWR_DIS;\ + type MPC_RMCM_3DLUT_MEM_LOW_PWR_MODE;\ + type MPC_RMCM_SHAPER_MEM_PWR_STATE;\ + type MPC_RMCM_3DLUT_MEM_PWR_STATE;\ + type MPC_RMCM_3DLUT_FL_SEL;\ + type MPC_RMCM_3DLUT_FL_DONE; \ + type MPC_RMCM_3DLUT_FL_SOFT_UNDERFLOW; \ + type MPC_RMCM_3DLUT_FL_HARD_UNDERFLOW; \ + type MPC_RMCM_CNTL; \ + type MPC_RMCM_TEST_DEBUG_INDEX;\ + type MPC_RMCM_TEST_DEBUG_WRITE_EN;\ + type MPC_RMCM_TEST_DEBUG_DATA + +#define MPC_RMCM_REG_VARIABLE_LIST_DCN42 \ + uint32_t MPC_RMCM_SHAPER_CONTROL[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_OFFSET_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_OFFSET_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_OFFSET_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_SCALE_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_SCALE_G_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_LUT_INDEX[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_LUT_DATA[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_START_CNTL_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_END_CNTL_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_0_1[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_2_3[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_4_5[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_6_7[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_8_9[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_10_11[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_12_13[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_14_15[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_16_17[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_18_19[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_20_21[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_22_23[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_24_25[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_26_27[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_28_29[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_30_31[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMA_REGION_32_33[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_START_CNTL_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_END_CNTL_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_0_1[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_2_3[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_4_5[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_6_7[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_8_9[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_10_11[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_12_13[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_14_15[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_16_17[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_18_19[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_20_21[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_22_23[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_24_25[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_26_27[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_28_29[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_30_31[MAX_RMCM]; \ + uint32_t MPC_RMCM_SHAPER_RAMB_REGION_32_33[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_MODE[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_INDEX[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_DATA[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_DATA_30BIT[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_READ_WRITE_CONTROL[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_OUT_NORM_FACTOR[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_R[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_G[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_OUT_OFFSET_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_COEF_FORMAT[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_MODE[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C11_C12_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C13_C14_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C21_C22_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C23_C24_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C31_C32_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C33_C34_A[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C11_C12_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C13_C14_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C21_C22_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C23_C24_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C31_C32_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_GAMUT_REMAP_C33_C34_B[MAX_RMCM]; \ + uint32_t MPC_RMCM_MEM_PWR_CTRL[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_FAST_LOAD_SELECT[MAX_RMCM]; \ + uint32_t MPC_RMCM_3DLUT_FAST_LOAD_STATUS[MAX_RMCM]; \ + uint32_t MPC_RMCM_CNTL[MAX_RMCM]; \ + uint32_t MPC_RMCM_TEST_DEBUG_INDEX[MAX_RMCM]; \ + uint32_t MPC_RMCM_TEST_DEBUG_DATA[MAX_RMCM] + +struct dcn42_rmcm_shift { + MPC_RMCM_REG_FIELD_LIST_DCN42(uint8_t); +}; + +struct dcn42_rmcm_mask { + MPC_RMCM_REG_FIELD_LIST_DCN42(uint32_t); +}; + +struct dcn42_rmcm_registers { + MPC_RMCM_REG_VARIABLE_LIST_DCN42; +}; + +struct dcn42_rmcm { + struct rmcm base; + const struct dcn42_rmcm_registers *rmcm_regs; + const struct dcn42_rmcm_shift *rmcm_shift; + const struct dcn42_rmcm_mask *rmcm_mask; +}; + +void dcn42_rmcm_construct( + struct dcn42_rmcm *rmcm42, + struct dc_context *ctx, + const struct dcn42_rmcm_registers *regs, + const struct dcn42_rmcm_shift *shift, + const struct dcn42_rmcm_mask *mask, + int inst); + +/* + * Register-programming helpers shared with the DCN60 RMCM component. They are bank-count + * agnostic (LUT data, power, fast load, MPCC routing), so the single-SRAM DCN60 variant + * only overrides bank selection. + */ +void rmcm42_power_on_shaper_3dlut(struct rmcm *rmcm, uint32_t rmcm_id, bool power_on); +uint32_t rmcm42_get_3dlut_width(const enum dc_cm_lut_size size); +void rmcm42_populate_lut(struct rmcm *rmcm, const enum MCM_LUT_ID id, + const union rmcm_lut_params params, bool lut_bank_a, int rmcm_id); +void rmcm42_update_3dlut_fast_load_select(struct rmcm *rmcm, int rmcm_id, int hubp_idx); +void rmcm42_get_3dlut_fast_load_status(struct rmcm *rmcm, int rmcm_id, + bool *done, bool *soft_underflow, bool *hard_underflow); +void rmcm42_read_state(struct rmcm *rmcm, int rmcm_inst, struct rmcm_state *s); +void rmcm42_connect_mpcc(struct rmcm *rmcm, int rmcm_id, int mpcc_id); + +#endif // __DC_RMCM_DCN42_H__ diff --git a/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.c b/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.c new file mode 100644 index 000000000000..df4d6476d70a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.c @@ -0,0 +1,251 @@ +/* + * 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 "dm_services.h" +#include "dc.h" +#include "reg_helper.h" + +#include "dcn/dcn_6_0_0_offset.h" +#include "dcn/dcn_6_0_0_sh_mask.h" + +#include "dcn60_rmcm.h" + +#define SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +/* DCN60 renamed MPC_RMCM_3DLUT_RAM_SEL to MPC_RMCM_3DLUT_READ_SEL */ +#define MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL__MPC_RMCM_3DLUT_RAM_SEL__SHIFT \ + MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL__MPC_RMCM_3DLUT_READ_SEL__SHIFT +#define MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL__MPC_RMCM_3DLUT_RAM_SEL_MASK \ + MPC_RMCM0_MPC_RMCM_3DLUT_READ_WRITE_CONTROL__MPC_RMCM_3DLUT_READ_SEL_MASK + +/* Resource-level macros needed by MPC_RMCM_REG_LIST_DCN42 */ +#define BASE_INNER(seg) ctx->dcn_reg_offsets[seg] + +#define BASE(seg) BASE_INNER(seg) + +#define SRII(reg_name, block, id)\ + REG_STRUCT.reg_name[id] = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + +static struct dcn42_rmcm_registers rmcm_regs; + +#define dcn_rmcm_regs_init() \ + MPC_RMCM_REG_LIST_DCN42(0), \ + MPC_RMCM_REG_LIST_DCN42(1) + +static const struct dcn42_rmcm_shift rmcm_shift = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(__SHIFT)}; + +static const struct dcn42_rmcm_mask rmcm_mask = { + MPC_RMCM_COMMON_MASK_SH_LIST_DCN42(_MASK)}; + +#define REG(reg)\ + rmcm42->rmcm_regs->reg + +#define CTX \ + rmcm42->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + rmcm42->rmcm_shift->field_name, rmcm42->rmcm_mask->field_name + +/* + * DCN60 RMCM LUT-bank programming (single-SRAM variant). + * + * DCN60 has one LUT SRAM per RMCM instance instead of the DCN42 A/B pair, and the 3DLUT + * read/write-control RAM select was renamed RAM_SEL -> READ_SEL. These overrides always + * target bank A and ignore lut_bank_a, so nothing can select a bank that does not exist. + * Everything bank-agnostic (LUT upload, fast load, power, MPCC routing) is reused from the + * DCN42 rmcm42_* helpers. + */ +static void rmcm60_get_lut_mode(struct rmcm *rmcm, + const enum MCM_LUT_ID id, + int rmcm_id, + bool *enable, + bool *lut_bank_a) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + uint32_t lut_mode = 0; + + *enable = false; + *lut_bank_a = true; /* single SRAM: always bank A */ + + switch (id) { + case MCM_LUT_SHAPER: + REG_GET(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_MODE_CURRENT, &lut_mode); + *enable = lut_mode != 0; + break; + case MCM_LUT_1DLUT: + /* RMCM doesn't support 1DLUT, return disabled */ + *enable = false; + break; + case MCM_LUT_3DLUT: + default: + REG_GET(MPC_RMCM_3DLUT_MODE[rmcm_id], + MPC_RMCM_3DLUT_MODE_CURRENT, &lut_mode); + *enable = lut_mode != 0; + break; + } +} + +static void rmcm60_program_lut_mode(struct rmcm *rmcm, + const enum MCM_LUT_ID id, + bool enable, + bool lut_bank_a, + const enum dc_cm_lut_size size, + uint16_t bias, + uint16_t scale, + int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + (void)bias; + (void)scale; + (void)lut_bank_a; /* single SRAM: always bank A (mode 1) */ + + switch (id) { + case MCM_LUT_3DLUT: + if (enable) { + uint32_t lut_size = rmcm42_get_3dlut_width(size); + + REG_UPDATE_2(MPC_RMCM_3DLUT_MODE[rmcm_id], + MPC_RMCM_3DLUT_MODE, 1, + MPC_RMCM_3DLUT_SIZE, lut_size); + + /* Default to 12-bit (30BIT_EN=0) for DMA path; + * populate_lut overrides for host-load path. + */ + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_WRITE_EN_MASK, 0xF); + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_30BIT_EN, 0); + } else { + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + REG_UPDATE(MPC_RMCM_3DLUT_MODE[rmcm_id], MPC_RMCM_3DLUT_MODE, 0); + } + break; + case MCM_LUT_SHAPER: + if (enable) { + REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_LUT_MODE, 1); + } else { + if (rmcm->ctx->dc->debug.enable_mem_low_power.bits.mpc) + rmcm42_power_on_shaper_3dlut(rmcm, rmcm_id, false); + REG_UPDATE(MPC_RMCM_SHAPER_CONTROL[rmcm_id], + MPC_RMCM_SHAPER_LUT_MODE, 0); + } + break; + case MCM_LUT_1DLUT: + /* RMCM doesn't support 1DLUT */ + break; + } +} + +static void rmcm60_program_lut_read_write_control(struct rmcm *rmcm, const enum MCM_LUT_ID id, + bool lut_bank_a, bool enabled, int rmcm_id) +{ + struct dcn42_rmcm *rmcm42 = TO_DCN42_RMCM(rmcm); + + (void)lut_bank_a; /* single SRAM: always bank A */ + + switch (id) { + case MCM_LUT_3DLUT: + REG_UPDATE(MPC_RMCM_3DLUT_MODE[rmcm_id], MPC_RMCM_3DLUT_MODE, + enabled ? 1 : 0); + + /* DCN60 renamed RAM_SEL -> READ_SEL; single bank -> always 0 */ + REG_UPDATE(MPC_RMCM_3DLUT_READ_WRITE_CONTROL[rmcm_id], + MPC_RMCM_3DLUT_RAM_SEL, 0); + break; + case MCM_LUT_SHAPER: + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK, 7); + + REG_UPDATE(MPC_RMCM_SHAPER_LUT_WRITE_EN_MASK[rmcm_id], + MPC_RMCM_SHAPER_LUT_WRITE_SEL, 0); + + REG_SET(MPC_RMCM_SHAPER_LUT_INDEX[rmcm_id], 0, + MPC_RMCM_SHAPER_LUT_INDEX, 0); + break; + default: + break; + } +} + +/* DCN60 has no RAM_SEL - it became READ_SEL, which the reused DCN42 read_state maps onto + * via the compat macro. Clear the duplicate so the reported state stays unambiguous. + */ +static void rmcm60_read_state(struct rmcm *rmcm, int rmcm_inst, + struct rmcm_state *s) +{ + rmcm42_read_state(rmcm, rmcm_inst, s); + s->regs.rmcm_3dlut_ram_sel = 0; +} + +static const struct rmcm_funcs dcn60_rmcm_funcs = { + .get_lut_mode = rmcm60_get_lut_mode, + .program_lut_mode = rmcm60_program_lut_mode, + .populate_lut = rmcm42_populate_lut, + .program_lut_read_write_control = rmcm60_program_lut_read_write_control, + .update_3dlut_fast_load_select = rmcm42_update_3dlut_fast_load_select, + .get_3dlut_fast_load_status = rmcm42_get_3dlut_fast_load_status, + .read_rmcm_state = rmcm60_read_state, + .connect_mpcc = rmcm42_connect_mpcc, +}; + +struct rmcm *dcn60_rmcm_create(struct dc_context *ctx, int inst) +{ + struct dcn42_rmcm *rmcm42 = kzalloc(sizeof(struct dcn42_rmcm), GFP_KERNEL); + + if (!rmcm42) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT rmcm_regs + dcn_rmcm_regs_init(); + + dcn42_rmcm_construct(rmcm42, + ctx, + &rmcm_regs, + &rmcm_shift, + &rmcm_mask, + inst); + + /* Override the DCN42 defaults with the DCN60 single-SRAM LUT-bank ops. */ + rmcm42->base.funcs = &dcn60_rmcm_funcs; + + return &rmcm42->base; +} + +void dcn60_rmcm_destroy(struct rmcm **rmcm) +{ + if (*rmcm) { + kfree(TO_DCN42_RMCM(*rmcm)); + *rmcm = NULL; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.h b/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.h new file mode 100644 index 000000000000..8a8b2db2762a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/rmcm/dcn60/dcn60_rmcm.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#ifndef __DC_RMCM_DCN60_H__ +#define __DC_RMCM_DCN60_H__ + +#include "dcn42/dcn42_rmcm.h" + +struct rmcm *dcn60_rmcm_create(struct dc_context *ctx, int inst); + +void dcn60_rmcm_destroy(struct rmcm **rmcm); + +#endif /* __DC_RMCM_DCN60_H__ */ From ad4ac1b342562d8cbbe90abcc2a7abe5583077c2 Mon Sep 17 00:00:00 2001 From: Ovidiu Bunea Date: Fri, 28 Aug 2026 16:07:55 -0400 Subject: [PATCH 007/114] drm/amd/display: Remove SDPIF_PORT_CONTROL programming for DCN31/35/42 [why & how] DCN IP team recommends SDPIF_PORT_CONTROL=0 as POR value for APUs. The new POR value ensures that DCHUBBUB will not disconnect the SDP port to DF unless it explicitly receives the pre_cstate_req signal. SDPIF_PORT_CONTROL=1 will only be used for IPS entry sequences, where it will be restored to 0 upon IPS exit. Reviewed-by: Leo Chen Signed-off-by: Ovidiu Bunea Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c | 5 ----- .../drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c | 7 +------ .../drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c | 13 +------------ 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c index cbcd22789013..35c94ca8335a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c @@ -1048,11 +1048,6 @@ void hubbub31_init(struct hubbub *hubbub) DISPCLK_R_DCHUBBUB_GATE_DIS, 1, DCFCLK_R_DCHUBBUB_GATE_DIS, 1); } - - /* - only the DCN will determine when to connect the SDP port - */ - REG_UPDATE(DCHUBBUB_SDPIF_CFG0, SDPIF_PORT_CONTROL, 1); } static const struct hubbub_funcs hubbub31_funcs = { .update_dchub = hubbub2_update_dchub, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c index 53744b000f5d..466bab437e37 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c @@ -531,12 +531,7 @@ void hubbub35_init(struct hubbub *hubbub) hubbub35_set_fgcg(hubbub2, hubbub->ctx->dc->debug.enable_fine_grain_clock_gating .bits.dchubbub); - /* - ignore the "df_pre_cstate_req" from the SDP port control. - only the DCN will determine when to connect the SDP port - */ - REG_UPDATE(DCHUBBUB_SDPIF_CFG0, - SDPIF_PORT_CONTROL, 1); + /*Set SDP's max outstanding request When set to 1: Max outstanding is 512 When set to 0: Max outstanding is 256 diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c index ddfc60e845a4..71bc926c7035 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c @@ -431,13 +431,6 @@ static void hubbub42_allow_self_refresh_control(struct hubbub *hubbub, bool allo DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_VALUE, 0, DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE, !allow); } -static void hubbub42_set_sdp_control(struct hubbub *hubbub, bool dc_control) -{ - struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); - - REG_UPDATE(DCHUBBUB_SDPIF_CFG0, - SDPIF_PORT_CONTROL, dc_control); -} static bool hubbub42_program_watermarks( struct hubbub *hubbub, @@ -449,9 +442,8 @@ static bool hubbub42_program_watermarks( struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); if (!safe_to_lower && hubbub->ctx->dc->debug.disable_stutter_for_wm_program) { - /* before raising watermarks, SDP control give to DF, stutter must be disabled */ + /* before raising watermarks, stutter must be disabled */ wm_pending = true; - hubbub42_set_sdp_control(hubbub, false); hubbub42_allow_self_refresh_control(hubbub, false); } if (hubbub42_program_urgent_watermarks(hubbub, watermarks, safe_to_lower)) @@ -478,9 +470,6 @@ static bool hubbub42_program_watermarks( if (safe_to_lower || hubbub->ctx->dc->debug.disable_stutter) hubbub42_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter); - if (safe_to_lower && hubbub->ctx->dc->debug.disable_stutter_for_wm_program) { - hubbub42_set_sdp_control(hubbub, true); - } hubbub32_force_usr_retraining_allow(hubbub, hubbub->ctx->dc->debug.force_usr_allow); return wm_pending; From 28197f5151c32bfb0718bff1957394a29dd67721 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:15:22 -0600 Subject: [PATCH 008/114] drm/amd/display: Test sink stream creation [WHAT] Add KUnit tests for create_stream_for_sink() covering the preferred mode timing recalculation, the scaled path that keeps the previous refresh rate, the automated test timing override, the DisplayPort VSC SDP colorimetry decision with and without the disable-colorimetry quirk, and the HDMI vendor specific infoframe path. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../tests/amdgpu_dm_connector_test.c | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index 7b6400ec8107..cb7117255267 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -27,6 +27,7 @@ #include "amdgpu_dm.h" #include "amdgpu_dm_connector.h" #include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_psr.h" #include "include/grph_object_id.h" #include "amdgpu_dm_kunit_test_helpers.h" #include "inc/link_service.h" @@ -4181,6 +4182,217 @@ static void dm_test_create_stream_existing_sink(struct kunit *test) dc_sink_release(sink); } +/* Append a copy of the context mode to the connector's mode list. */ +static void dm_test_stream_add_mode(struct kunit *test, struct dm_test_stream_ctx *ctx, + u32 type) +{ + struct drm_display_mode *mode = drm_mode_create(ctx->drm); + + KUNIT_ASSERT_NOT_NULL(test, mode); + drm_mode_copy(mode, ctx->mode); + mode->type = type; + list_add_tail(&mode->head, &ctx->aconnector->base.modes); +} + +/* Give the context mode a full set of CRTC timings. */ +static void dm_test_stream_set_timings(struct dm_test_stream_ctx *ctx) +{ + ctx->mode->hsync_start = 2008; + ctx->mode->hsync_end = 2052; + ctx->mode->htotal = 2200; + ctx->mode->vsync_start = 1084; + ctx->mode->vsync_end = 1089; + ctx->mode->vtotal = 1125; +} + +/* Create a real dc_sink of @signal owned by the caller. */ +static struct dc_sink *dm_test_stream_add_sink(struct kunit *test, + struct dm_test_stream_ctx *ctx, + enum signal_type signal) +{ + struct dc_sink_init_data sink_init = { 0 }; + struct dc_sink *sink; + + sink_init.link = ctx->link; + sink_init.sink_signal = signal; + sink = dc_sink_create(&sink_init); + KUNIT_ASSERT_NOT_NULL(test, sink); + sink->sink_signal = signal; + ctx->aconnector->dc_sink = sink; + + return sink; +} + +/** + * dm_test_create_stream_preferred_mode - Test the preferred mode drives the timing + * @test: The KUnit test context + * + * A preferred mode in the connector's mode list is used to recompute the CRTC + * timings of the requested mode before the stream properties are filled in. + */ +static void dm_test_create_stream_preferred_mode(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + dm_test_stream_set_timings(ctx); + dm_test_stream_add_mode(test, ctx, DRM_MODE_TYPE_PREFERRED); + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->timing.h_addressable, 1920); + KUNIT_EXPECT_EQ(test, (int)stream->timing.v_addressable, 1080); + KUNIT_EXPECT_EQ(test, (int)stream->timing.h_total, 2200); + KUNIT_EXPECT_EQ(test, (int)stream->timing.v_total, 1125); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_scaled_keeps_refresh - Test scaling reuses the old timing + * @test: The KUnit test context + * + * With scaling enabled and an unchanged refresh rate the stream properties are + * filled from the old stream so the VIC and sync polarities are preserved. + */ +static void dm_test_create_stream_scaled_keeps_refresh(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + dm_test_stream_set_timings(ctx); + dm_test_stream_add_mode(test, ctx, DRM_MODE_TYPE_PREFERRED); + ctx->dm_state->scaling = RMX_FULL; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->timing.h_addressable, 1920); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_timing_override - Test the automated test timing override + * @test: The KUnit test context + * + * A connector carrying a requested timing from an automated test replaces the + * timing derived from the mode. + */ +static void dm_test_create_stream_timing_override(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_crtc_timing *timing; + struct dc_stream_state *stream; + + timing = kunit_kzalloc(test, sizeof(*timing), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, timing); + timing->h_addressable = 1280; + timing->v_addressable = 720; + timing->display_color_depth = COLOR_DEPTH_101010; + + ctx->aconnector->timing_changed = true; + ctx->aconnector->timing_requested = timing; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->timing.h_addressable, 1280); + KUNIT_EXPECT_EQ(test, (int)stream->timing.display_color_depth, + (int)COLOR_DEPTH_101010); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_dp_vsc_colorimetry - Test the DP VSC SDP colorimetry decision + * @test: The KUnit test context + * + * A DisplayPort sink on a DPCD 1.4 link advertising VSC SDP colorimetry, and + * without the disable-colorimetry quirk, enables VSC SDP colorimetry and + * arms the self refresh entry delay. + */ +static void dm_test_create_stream_dp_vsc_colorimetry(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + struct dc_sink *sink; + + sink = dm_test_stream_add_sink(test, ctx, SIGNAL_TYPE_DISPLAY_PORT); + ctx->link->dpcd_caps.dpcd_rev.raw = 0x14; + ctx->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED = 1; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->signal, (int)SIGNAL_TYPE_DISPLAY_PORT); + KUNIT_EXPECT_TRUE(test, stream->use_vsc_sdp_for_colorimetry); + KUNIT_EXPECT_EQ(test, ctx->aconnector->sr_skip_count, AMDGPU_DM_PSR_ENTRY_DELAY); + + dc_stream_release(stream); + dc_sink_release(sink); +} + +/** + * dm_test_create_stream_dp_colorimetry_quirk - Test the disable colorimetry quirk + * @test: The KUnit test context + */ +static void dm_test_create_stream_dp_colorimetry_quirk(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + struct dc_sink *sink; + + sink = dm_test_stream_add_sink(test, ctx, SIGNAL_TYPE_DISPLAY_PORT); + sink->edid_caps.panel_patch.disable_colorimetry = true; + ctx->link->dpcd_caps.dpcd_rev.raw = 0x14; + ctx->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED = 1; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_FALSE(test, stream->use_vsc_sdp_for_colorimetry); + + dc_stream_release(stream); + dc_sink_release(sink); +} + +/** + * dm_test_create_stream_hdmi_vsif - Test the HDMI vendor specific infoframe + * @test: The KUnit test context + * + * An HDMI sink takes the HDMI Forum vendor specific infoframe path. A plain + * 2D, non-HDMI-VIC timing carries no payload, so the packet stays invalid. + */ +static void dm_test_create_stream_hdmi_vsif(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + struct dc_sink *sink; + + ctx->link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink = dm_test_stream_add_sink(test, ctx, SIGNAL_TYPE_HDMI_TYPE_A); + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->signal, (int)SIGNAL_TYPE_HDMI_TYPE_A); + KUNIT_EXPECT_FALSE(test, stream->vsp_infopacket.valid); + + dc_stream_release(stream); + dc_sink_release(sink); +} + /* Tests for amdgpu_dm_connector_detect() */ /* @@ -8730,6 +8942,12 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_create_stream_virtual_signal), KUNIT_CASE(dm_test_create_stream_scaling_src), KUNIT_CASE(dm_test_create_stream_existing_sink), + KUNIT_CASE(dm_test_create_stream_preferred_mode), + KUNIT_CASE(dm_test_create_stream_scaled_keeps_refresh), + KUNIT_CASE(dm_test_create_stream_timing_override), + KUNIT_CASE(dm_test_create_stream_dp_vsc_colorimetry), + KUNIT_CASE(dm_test_create_stream_dp_colorimetry_quirk), + KUNIT_CASE(dm_test_create_stream_hdmi_vsif), /* amdgpu_dm_connector_detect */ KUNIT_CASE(dm_test_detect_force_on), KUNIT_CASE(dm_test_detect_force_on_digital), From 94be315afbd42b588d887f3e12a8b37475f651a4 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:16:49 -0600 Subject: [PATCH 009/114] drm/amd/display: Test connector init helper [WHAT] Add KUnit tests for amdgpu_dm_connector_init_helper() covering the common connector defaults and attached properties, the HDMI, DisplayPort, DVI-D, VGA, eDP and unhandled connector types, an MST branch connector that skips the root-only properties, the content protection property and the default HPD debounce delay. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_connector.c | 1 + .../tests/amdgpu_dm_connector_test.c | 355 ++++++++++++++++++ 2 files changed, 356 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index c1b5394617a3..7d6d4dd22831 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -3322,6 +3322,7 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, } } } +EXPORT_IF_KUNIT(amdgpu_dm_connector_init_helper); STATIC_IFN_KUNIT int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index cb7117255267..b232a0c7389b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -27,6 +27,7 @@ #include "amdgpu_dm.h" #include "amdgpu_dm_connector.h" #include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_hdcp.h" #include "amdgpu_dm_psr.h" #include "include/grph_object_id.h" #include "amdgpu_dm_kunit_test_helpers.h" @@ -8719,6 +8720,349 @@ static void dm_test_frl_poll_retrains(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_test_frl_detect_calls, 1); } +/* Tests for amdgpu_dm_connector_init_helper() */ + +/* + * Build an amdgpu_dm_connector on a kunit drm_device embedded in an + * amdgpu_device, with the amdgpu mode properties created exactly as + * amdgpu_dm_mode_config_init() does so the helper has real properties to + * attach. The dc_link carries a link encoder and leaves DIG mapping + * inflexible, so link_enc_cfg_get_link_enc() resolves without a dc instance. + */ +struct dm_test_init_helper_ctx { + struct amdgpu_device *adev; + struct drm_device *drm; + struct amdgpu_display_manager *dm; + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; +}; + +/* + * Stand in for amdgpu_display_modeset_create_props(), which the test module + * cannot link against. Only the properties the init helper attaches are + * created; underscan is a plain range because only attachment is under test. + */ +static void dm_test_create_mode_props(struct kunit *test, struct amdgpu_device *adev) +{ + struct amdgpu_mode_info *mode_info = &adev->mode_info; + struct drm_device *drm = adev_to_drm(adev); + + KUNIT_ASSERT_EQ(test, drm_mode_create_scaling_mode_property(drm), 0); + + mode_info->underscan_property = + drm_property_create_range(drm, 0, "underscan", 0, 1); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_property); + mode_info->underscan_hborder_property = + drm_property_create_range(drm, 0, "underscan hborder", 0, 128); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_hborder_property); + mode_info->underscan_vborder_property = + drm_property_create_range(drm, 0, "underscan vborder", 0, 128); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_vborder_property); +} + +static struct dm_test_init_helper_ctx * +dm_test_init_helper_ctx_alloc(struct kunit *test, int connector_type) +{ + struct dm_test_init_helper_ctx *ctx; + 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(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + + ctx->adev = drm_to_adev(ctx->drm); + ctx->adev->dev = dev; + dm_test_create_mode_props(test, ctx->adev); + + ctx->dm = &ctx->adev->dm; + ctx->dm->adev = ctx->adev; + ctx->dm->ddev = ctx->drm; + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->link->link_enc = kunit_kzalloc(test, sizeof(*ctx->link->link_enc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link->link_enc); + ctx->aconnector = drmm_kzalloc(ctx->drm, 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, connector_type, + NULL), 0); + + return ctx; +} + +/* True when @prop is attached to the connector's mode object. */ +static bool dm_test_has_prop(struct drm_connector *connector, struct drm_property *prop) +{ + struct drm_object_properties *props = connector->base.properties; + int i; + + if (!prop) + return false; + + for (i = 0; i < props->count; i++) + if (props->properties[i] == prop) + return true; + + return false; +} + +/** + * dm_test_init_helper_common_defaults - Test the connector defaults and properties + * @test: The KUnit test context + * + * The helper resets the connector state, records the link and its index, and + * attaches the scaling mode and the three underscan properties that every + * connector type receives. + */ +static void dm_test_init_helper_common_defaults(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + struct drm_connector *connector = &ctx->aconnector->base; + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_HDMIA, ctx->link, 3); + + KUNIT_ASSERT_NOT_NULL(test, connector->state); + KUNIT_EXPECT_EQ(test, ctx->aconnector->connector_id, 3); + KUNIT_EXPECT_EQ(test, ctx->aconnector->bl_idx, -1); + KUNIT_EXPECT_PTR_EQ(test, ctx->aconnector->dc_link, ctx->link); + KUNIT_EXPECT_EQ(test, ctx->aconnector->audio_inst, -1); + KUNIT_EXPECT_FALSE(test, ctx->aconnector->pack_sdp_v1_3); + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->as_type, (int)ADAPTIVE_SYNC_TYPE_NONE); + KUNIT_EXPECT_FALSE(test, connector->interlace_allowed); + KUNIT_EXPECT_FALSE(test, connector->doublescan_allowed); + KUNIT_EXPECT_FALSE(test, connector->stereo_allowed); + KUNIT_EXPECT_EQ(test, connector->dpms, DRM_MODE_DPMS_OFF); + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->hpd.hpd, (int)AMDGPU_HPD_NONE); + KUNIT_EXPECT_EQ(test, ctx->aconnector->hdmi_hpd_debounce_delay_ms, 0); + + /* The link encoder advertises no YCbCr 4:2:0 support. */ + KUNIT_EXPECT_FALSE(test, connector->ycbcr_420_allowed); + + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, ctx->drm->mode_config.scaling_mode_property)); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, ctx->adev->mode_info.underscan_property)); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, + ctx->adev->mode_info.underscan_hborder_property)); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, + ctx->adev->mode_info.underscan_vborder_property)); +} + +/** + * dm_test_init_helper_hdmi - Test the HDMI connector wiring + * @test: The KUnit test context + * + * HDMI polls on hotplug, takes YCbCr 4:2:0 support straight from the link + * encoder features and gets the max bpc, content type, colorspace, HDR + * metadata and VRR capable properties. + */ +static void dm_test_init_helper_hdmi(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + struct drm_connector *connector = &ctx->aconnector->base; + + ctx->link->link_enc->features.hdmi_ycbcr420_supported = true; + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, (int)connector->polled, (int)DRM_CONNECTOR_POLL_HPD); + KUNIT_EXPECT_TRUE(test, connector->ycbcr_420_allowed); + KUNIT_ASSERT_NOT_NULL(test, connector->state); + KUNIT_EXPECT_EQ(test, connector->state->max_bpc, 16); + KUNIT_EXPECT_EQ(test, connector->state->max_requested_bpc, 16); + KUNIT_EXPECT_NOT_NULL(test, connector->max_bpc_property); + KUNIT_EXPECT_NOT_NULL(test, connector->broadcast_rgb_property); + KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property); + KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, ctx->drm->mode_config.content_type_property)); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, + ctx->drm->mode_config.hdr_output_metadata_property)); +} + +/** + * dm_test_init_helper_dp - Test the DisplayPort connector wiring + * @test: The KUnit test context + * + * DisplayPort re-resolves the link encoder through the encoder configuration + * and takes YCbCr 4:2:0 support from its DP feature bit. + */ +static void dm_test_init_helper_dp(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort); + struct drm_connector *connector = &ctx->aconnector->base; + + ctx->link->link_enc->features.dp_ycbcr420_supported = true; + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_DisplayPort, ctx->link, 1); + + KUNIT_EXPECT_EQ(test, (int)connector->polled, (int)DRM_CONNECTOR_POLL_HPD); + KUNIT_EXPECT_TRUE(test, connector->ycbcr_420_allowed); + KUNIT_EXPECT_NOT_NULL(test, connector->broadcast_rgb_property); + KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property); + KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property); +} + +/** + * dm_test_init_helper_dp_mst_root - Test an MST branch connector skips properties + * @test: The KUnit test context + * + * A connector below an MST root gets neither the broadcast RGB, max bpc, + * colorspace nor VRR capable property, because those live on the root. + */ +static void dm_test_init_helper_dp_mst_root(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort); + struct drm_connector *connector = &ctx->aconnector->base; + + ctx->aconnector->mst_root = ctx->aconnector; + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_DisplayPort, ctx->link, 0); + + KUNIT_EXPECT_NULL(test, connector->broadcast_rgb_property); + KUNIT_EXPECT_NULL(test, connector->max_bpc_property); + KUNIT_EXPECT_NULL(test, connector->colorspace_property); + KUNIT_EXPECT_NULL(test, connector->vrr_capable_property); +} + +/** + * dm_test_init_helper_dvid - Test DVI-D polls on hotplug + * @test: The KUnit test context + */ +static void dm_test_init_helper_dvid(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_DVID); + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_DVID, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled, (int)DRM_CONNECTOR_POLL_HPD); + KUNIT_EXPECT_FALSE(test, ctx->aconnector->base.ycbcr_420_allowed); +} + +/** + * dm_test_init_helper_vga - Test VGA polls on connect and disconnect + * @test: The KUnit test context + */ +static void dm_test_init_helper_vga(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_VGA); + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_VGA, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled, + (int)(DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)); +} + +/** + * dm_test_init_helper_unpolled_default - Test an unhandled type is left unpolled + * @test: The KUnit test context + */ +static void dm_test_init_helper_unpolled_default(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_Composite); + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_Composite, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled, 0); + KUNIT_EXPECT_NULL(test, ctx->aconnector->base.colorspace_property); + KUNIT_EXPECT_NULL(test, ctx->aconnector->base.vrr_capable_property); +} + +/** + * dm_test_init_helper_edp - Test eDP gets the panel type property + * @test: The KUnit test context + * + * eDP shares the DisplayPort colorspace and HDR property set and additionally + * receives the panel type property. No privacy screen provider is registered + * in the test environment, so the lookup fails with -ENODEV and is ignored. + */ +static void dm_test_init_helper_edp(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + struct drm_connector *connector = &ctx->aconnector->base; + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_eDP, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, (int)connector->polled, 0); + KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property); + KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property); + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, ctx->drm->mode_config.panel_type_property)); + KUNIT_EXPECT_NULL(test, connector->privacy_screen); +} + +/** + * dm_test_init_helper_hdcp_property - Test the content protection property + * @test: The KUnit test context + * + * The content protection property is only attached when a HDCP workqueue was + * created for the device. + */ +static void dm_test_init_helper_hdcp_property(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + struct drm_connector *connector = &ctx->aconnector->base; + + ctx->adev->dm.hdcp_workqueue = kunit_kzalloc(test, sizeof(struct hdcp_workqueue), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->adev->dm.hdcp_workqueue); + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0); + + KUNIT_EXPECT_TRUE(test, + dm_test_has_prop(connector, + ctx->drm->mode_config.content_protection_property)); +} + +/** + * dm_test_init_helper_hpd_debounce_disabled - Test the default HPD debounce delay + * @test: The KUnit test context + * + * With the hdmi_hpd_debounce_delay_ms module parameter left at its default the + * debounce work is not armed and the delay stays zero. + */ +static void dm_test_init_helper_hpd_debounce_disabled(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector, + DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->hdmi_hpd_debounce_delay_ms, 0); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -9102,6 +9446,17 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_frl_skips_zero_rate), KUNIT_CASE(dm_test_frl_poll_no_update), KUNIT_CASE(dm_test_frl_poll_retrains), + /* amdgpu_dm_connector_init_helper */ + KUNIT_CASE(dm_test_init_helper_common_defaults), + KUNIT_CASE(dm_test_init_helper_hdmi), + KUNIT_CASE(dm_test_init_helper_dp), + KUNIT_CASE(dm_test_init_helper_dp_mst_root), + KUNIT_CASE(dm_test_init_helper_dvid), + KUNIT_CASE(dm_test_init_helper_vga), + KUNIT_CASE(dm_test_init_helper_unpolled_default), + KUNIT_CASE(dm_test_init_helper_edp), + KUNIT_CASE(dm_test_init_helper_hdcp_property), + KUNIT_CASE(dm_test_init_helper_hpd_debounce_disabled), {} }; From 09c301c02679b12fedc05b00c32aa6635a4b62e3 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:17:35 -0600 Subject: [PATCH 010/114] drm/amd/display: Test HDMI connector init [WHAT] Add a KUnit test for amdgpu_dm_initialize_hdmi_connector() verifying that a CEC notifier is created for the connector. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_connector.c | 1 + .../tests/amdgpu_dm_connector_test.c | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 7d6d4dd22831..f06287163122 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -3427,6 +3427,7 @@ int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector) return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_initialize_hdmi_connector); /* * Note: this function assumes that dc_link_detect() was called for the diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index b232a0c7389b..62edf19a6345 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -9063,6 +9063,24 @@ static void dm_test_init_helper_hpd_debounce_disabled(struct kunit *test) KUNIT_EXPECT_EQ(test, ctx->aconnector->hdmi_hpd_debounce_delay_ms, 0); } +/* Tests for amdgpu_dm_initialize_hdmi_connector() */ + +/** + * dm_test_initialize_hdmi_cec_registers - Test the CEC notifier is registered + * @test: The KUnit test context + * + * Without DC_DISABLE_HDMI_CEC in the debug mask a CEC notifier is created for + * the connector. + */ +static void dm_test_initialize_hdmi_cec_registers(struct kunit *test) +{ + struct dm_test_init_helper_ctx *ctx = + dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_initialize_hdmi_connector(ctx->aconnector), 0); + KUNIT_EXPECT_NOT_NULL(test, ctx->aconnector->notifier); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -9457,6 +9475,8 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_init_helper_edp), KUNIT_CASE(dm_test_init_helper_hdcp_property), KUNIT_CASE(dm_test_init_helper_hpd_debounce_disabled), + /* amdgpu_dm_initialize_hdmi_connector */ + KUNIT_CASE(dm_test_initialize_hdmi_cec_registers), {} }; From 3251f8cb879f417c7bceb443fc799688326cd75d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:18:22 -0600 Subject: [PATCH 011/114] drm/amd/display: Test FreeSync caps update [WHAT] Add KUnit tests for amdgpu_dm_update_freesync_caps() covering the DisplayPort invalid-MSA refresh range along with its narrow-range and valid-MSA-required rejections, an eDP panel advertising replay, the HDMI AMD VSDB range with and without a parser, a whitelisted DP to HDMI converter, the forced FreeSync minimum quirk, the MCCS capability withdrawal and the replay teardown. v2: rebase (Alex) Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_connector.c | 1 + .../tests/amdgpu_dm_connector_test.c | 362 ++++++++++++++++++ 2 files changed, 363 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index f06287163122..29137076c826 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -3894,3 +3894,4 @@ update: drm_connector_set_vrr_capable_property(connector, freesync_capable); } +EXPORT_IF_KUNIT(amdgpu_dm_update_freesync_caps); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index 62edf19a6345..6d4850e3df21 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -9081,6 +9081,359 @@ static void dm_test_initialize_hdmi_cec_registers(struct kunit *test) KUNIT_EXPECT_NOT_NULL(test, ctx->aconnector->notifier); } +/* Tests for amdgpu_dm_update_freesync_caps() */ + +static void dm_test_fs_free_edid(void *data) +{ + drm_edid_free(data); +} + +/* + * Build a two block EDID: the minimal base block with its extension count set + * to one, followed by @ext, with both checksums recomputed so drm_edid_raw() + * and the DRM EDID parsers accept it. + */ +static const struct drm_edid *dm_test_fs_edid_alloc(struct kunit *test, const u8 *ext) +{ + const struct drm_edid *drm_edid; + u8 *raw; + int i; + + raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, raw); + memcpy(raw, dm_test_uad_edid, EDID_LENGTH); + memcpy(raw + EDID_LENGTH, ext, EDID_LENGTH); + raw[EDID_LENGTH - 2] = 1; + + for (i = 0; i < 2; i++) { + u8 *block = raw + i * EDID_LENGTH; + u8 sum = 0; + int j; + + for (j = 0; j < EDID_LENGTH - 1; j++) + sum += block[j]; + block[EDID_LENGTH - 1] = -sum; + } + + drm_edid = drm_edid_alloc(raw, 2 * EDID_LENGTH); + KUNIT_ASSERT_NOT_NULL(test, drm_edid); + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, dm_test_fs_free_edid, + (void *)drm_edid), 0); + + return drm_edid; +} + +/* + * A DisplayID extension holding a dynamic video timing range descriptor, which + * is what parse_edid_displayid_vrr() scans for. + */ +static const struct drm_edid * +dm_test_fs_edid_displayid(struct kunit *test, u8 min_vfreq, u8 max_vfreq) +{ + u8 ext[EDID_LENGTH] = {0}; + + ext[0] = DM_TEST_DISPLAYID_EXT; + ext[1] = 0x25; + ext[2] = 0x00; + ext[3] = 9; + ext[10] = min_vfreq; + ext[11] = max_vfreq; + + return dm_test_fs_edid_alloc(test, ext); +} + +/* + * A CTA-861 extension with no data blocks, enough for parse_hdmi_amd_vsdb() to + * accept the block and hand it to the (mocked) DMCU parser. + */ +static const struct drm_edid *dm_test_fs_edid_cea(struct kunit *test) +{ + u8 ext[EDID_LENGTH] = {0}; + + ext[0] = DM_TEST_CEA_EXT; + ext[1] = 3; + ext[2] = 4; + + return dm_test_fs_edid_alloc(test, ext); +} + +/* + * A CTA-861 extension carrying a 15-byte AMD VSDB v3 payload, which + * drm_parse_amd_vsdb() turns into connector->display_info.amd_vsdb and + * get_amd_vsdb() then reads back. + */ +static const struct drm_edid * +dm_test_fs_edid_amd_vsdb(struct kunit *test, u8 feature_caps) +{ + u8 ext[EDID_LENGTH] = {0}; + + ext[0] = DM_TEST_CEA_EXT; + ext[1] = 3; + ext[2] = 4 + 1 + 15; + ext[4] = (3 << 5) | 15; /* vendor data block, 15-byte payload */ + ext[5] = 0x1a; /* AMD IEEE OUI, LSB first */ + ext[8] = 0x03; /* AMD VSDB payload version */ + ext[9] = feature_caps; + + return dm_test_fs_edid_alloc(test, ext); +} + +struct dm_test_fs_caps_ctx { + struct amdgpu_device *adev; + struct drm_device *drm; + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; + struct dc_sink *sink; + struct resource_pool *pool; +}; + +/* + * Build a connector that clears every guard at the top of + * amdgpu_dm_update_freesync_caps(): a reset connector state, a dc_sink whose + * dc_context reports a VRR capable DCE version, and a non-NULL freesync + * module. The resource pool carries no DMCU, so tests that need the AMD VSDB + * parser to succeed install one. + */ +static struct dm_test_fs_caps_ctx * +dm_test_fs_caps_ctx_alloc(struct kunit *test, enum signal_type sink_signal) +{ + struct dm_test_fs_caps_ctx *ctx; + struct dc_context *dc_ctx; + struct device *dev; + struct dc *dc; + + 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(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + ctx->adev = drm_to_adev(ctx->drm); + ctx->adev->dm.adev = ctx->adev; + mutex_init(&ctx->adev->dm.dc_lock); + + ctx->aconnector = drmm_kzalloc(ctx->drm, 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_DisplayPort, NULL), 0); + amdgpu_dm_connector_funcs_reset(&ctx->aconnector->base); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector->base.state); + KUNIT_ASSERT_EQ(test, + drm_connector_attach_vrr_capable_property(&ctx->aconnector->base), 0); + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc_ctx); + dc_ctx->driver_context = ctx->adev; + dc_ctx->dce_version = DCE_VERSION_8_0; + + ctx->pool = kunit_kzalloc(test, sizeof(*ctx->pool), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->pool); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + dc->ctx = dc_ctx; + dc->res_pool = ctx->pool; + ctx->adev->dm.dc = dc; + + ctx->adev->dm.freesync_module = + kunit_kzalloc(test, sizeof(struct mod_freesync), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->adev->dm.freesync_module); + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->aconnector->dc_link = ctx->link; + + ctx->sink = kunit_kzalloc(test, sizeof(*ctx->sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->sink); + ctx->sink->ctx = dc_ctx; + ctx->sink->sink_signal = sink_signal; + ctx->aconnector->dc_sink = ctx->sink; + + return ctx; +} + +static bool dm_test_fs_caps_capable(struct dm_test_fs_caps_ctx *ctx) +{ + return to_dm_connector_state(ctx->aconnector->base.state)->freesync_capable; +} + +/** + * dm_test_fs_caps_dp_msa_range - Test a DP sink with an invalid-MSA range + * @test: The KUnit test context + * + * A DisplayPort sink allowing invalid MSA timing takes its refresh range from + * the monitor range that parse_edid_displayid_vrr() recovered, and a range + * wider than 10Hz marks the connector FreeSync capable. + */ +static void dm_test_fs_caps_dp_msa_range(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 40, 144); + + ctx->link->dpcd_caps.allow_invalid_MSA_timing_param = true; + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->min_vfreq, 40); + KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 144); + KUNIT_EXPECT_TRUE(test, dm_test_fs_caps_capable(ctx)); +} + +/** + * dm_test_fs_caps_dp_narrow_range - Test a range of 10Hz or less is rejected + * @test: The KUnit test context + */ +static void dm_test_fs_caps_dp_narrow_range(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 60, 65); + + ctx->link->dpcd_caps.allow_invalid_MSA_timing_param = true; + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->min_vfreq, 60); + KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 65); + KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx)); +} + +/** + * dm_test_fs_caps_dp_msa_not_allowed - Test a DP sink requiring valid MSA + * @test: The KUnit test context + * + * Without allow_invalid_MSA_timing_param the reported range is ignored and the + * connector keeps a zero refresh range. + */ +static void dm_test_fs_caps_dp_msa_not_allowed(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 40, 144); + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->min_vfreq, 0); + KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 0); + KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx)); +} + +/** + * dm_test_fs_caps_edp_replay_mode - Test an eDP panel advertising replay + * @test: The KUnit test context + * + * An AMD VSDB with the replay feature bit set records the replay mode and its + * VSDB version on the connector and selects the eDP adaptive sync type. + */ +static void dm_test_fs_caps_edp_replay_mode(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_EDP); + const struct drm_edid *drm_edid = dm_test_fs_edid_amd_vsdb(test, 0x40); + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_TRUE(test, ctx->aconnector->vsdb_info.replay_mode); + KUNIT_EXPECT_EQ(test, ctx->aconnector->vsdb_info.amd_vsdb_version, 3); + KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->as_type, (int)ADAPTIVE_SYNC_TYPE_EDP); +} + +/** + * dm_test_fs_caps_hdmi_no_vsdb - Test an HDMI sink with no AMD VSDB + * @test: The KUnit test context + * + * Without a DMCU the CEA parse fails, so no refresh range is recorded. + */ +static void dm_test_fs_caps_hdmi_no_vsdb(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_HDMI_TYPE_A); + const struct drm_edid *drm_edid = dm_test_fs_edid_cea(test); + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_FALSE(test, ctx->aconnector->vsdb_info.freesync_supported); + KUNIT_EXPECT_EQ(test, ctx->aconnector->max_vfreq, 0); + KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx)); +} + +/** + * dm_test_fs_caps_force_min_hz_quirk - Test the forced FreeSync minimum quirk + * @test: The KUnit test context + * + * A quirked panel overrides the minimum refresh rate on both the connector and + * the published monitor range once FreeSync is otherwise supported. + */ +static void dm_test_fs_caps_force_min_hz_quirk(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 40, 144); + struct drm_connector *connector = &ctx->aconnector->base; + + ctx->link->dpcd_caps.allow_invalid_MSA_timing_param = true; + ctx->sink->edid_caps.panel_patch.force_freesync_min_hz = 50; + + amdgpu_dm_update_freesync_caps(connector, drm_edid, false); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->min_vfreq, 50); + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 50); +} + +/** + * dm_test_fs_caps_mccs_clears_capability - Test MCCS withdraws FreeSync support + * @test: The KUnit test context + * + * When the sink advertises a FreeSync VCP code but MCCS reports no support, + * the connector loses its FreeSync capability. + */ +static void dm_test_fs_caps_mccs_clears_capability(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 40, 144); + + ctx->link->dpcd_caps.allow_invalid_MSA_timing_param = true; + /* A non-DP, non-HDMI link keeps dm_helpers_read_mccs_caps() off the wire. */ + ctx->link->connector_signal = SIGNAL_TYPE_VIRTUAL; + ctx->sink->edid_caps.freesync_vcp_code = 0x60; + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, true); + + KUNIT_EXPECT_FALSE(test, ctx->sink->mccs_caps.freesync_supported); + KUNIT_EXPECT_FALSE(test, dm_test_fs_caps_capable(ctx)); +} + +/** + * dm_test_fs_caps_disables_replay - Test replay is dropped without FreeSync + * @test: The KUnit test context + * + * A link advertising replay support has it withdrawn when the connector ends + * up without FreeSync capability. + */ +static void dm_test_fs_caps_disables_replay(struct kunit *test) +{ + struct dm_test_fs_caps_ctx *ctx = + dm_test_fs_caps_ctx_alloc(test, SIGNAL_TYPE_DISPLAY_PORT); + const struct drm_edid *drm_edid = dm_test_fs_edid_displayid(test, 40, 144); + + ctx->link->replay_settings.config.replay_supported = true; + ctx->link->replay_settings.replay_feature_enabled = true; + + amdgpu_dm_update_freesync_caps(&ctx->aconnector->base, drm_edid, false); + + KUNIT_EXPECT_FALSE(test, ctx->link->replay_settings.config.replay_supported); + KUNIT_EXPECT_FALSE(test, ctx->link->replay_settings.replay_feature_enabled); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -9477,6 +9830,15 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_init_helper_hpd_debounce_disabled), /* amdgpu_dm_initialize_hdmi_connector */ KUNIT_CASE(dm_test_initialize_hdmi_cec_registers), + /* amdgpu_dm_update_freesync_caps */ + KUNIT_CASE(dm_test_fs_caps_dp_msa_range), + KUNIT_CASE(dm_test_fs_caps_dp_narrow_range), + KUNIT_CASE(dm_test_fs_caps_dp_msa_not_allowed), + KUNIT_CASE(dm_test_fs_caps_edp_replay_mode), + KUNIT_CASE(dm_test_fs_caps_hdmi_no_vsdb), + KUNIT_CASE(dm_test_fs_caps_force_min_hz_quirk), + KUNIT_CASE(dm_test_fs_caps_mccs_clears_capability), + KUNIT_CASE(dm_test_fs_caps_disables_replay), {} }; From 7db85e4b053c487f54e4d343bd5be965888cc047 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:19:09 -0600 Subject: [PATCH 012/114] drm/amd/display: Test connector init [WHAT] Add KUnit tests for amdgpu_dm_connector_init() covering a HDMI link that is fully brought up, a DVI link that maps to a DVI-D connector without a CEC notifier, and the get_modes() helper hook it installs. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_connector.c | 1 + .../tests/amdgpu_dm_connector_test.c | 190 ++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 29137076c826..5e95c8d5bb46 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -3505,6 +3505,7 @@ out_free: } return res; } +EXPORT_IF_KUNIT(amdgpu_dm_connector_init); static int dm_force_atomic_commit(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index 6d4850e3df21..d03e0a67c42a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -9434,6 +9435,191 @@ static void dm_test_fs_caps_disables_replay(struct kunit *test) KUNIT_EXPECT_FALSE(test, ctx->link->replay_settings.replay_feature_enabled); } +/* Tests for amdgpu_dm_connector_init() and the get_modes() helper hook */ + +/* + * amdgpu_dm_create_i2c() parents the adapter on adev->pdev->dev, so + * devm_i2c_add_adapter() needs a registered device there. Provide one by + * registering the device embedded in a KUnit allocated pci_dev. + */ +static void dm_test_conn_init_release_dev(struct device *dev) +{ + /* Backing storage is KUnit managed, so there is nothing to free. */ +} + +static void dm_test_conn_init_unregister_dev(void *data) +{ + device_unregister(data); +} + +struct dm_test_conn_init_ctx { + struct amdgpu_device *adev; + struct drm_device *drm; + struct amdgpu_display_manager *dm; + struct amdgpu_dm_connector *aconnector; + struct amdgpu_encoder *aencoder; + struct dc_link *link; +}; + +static struct dm_test_conn_init_ctx * +dm_test_conn_init_ctx_alloc(struct kunit *test, enum signal_type signal) +{ + struct dm_test_conn_init_ctx *ctx; + struct link_service *link_srv; + struct dc_context *dc_ctx; + struct ddc_service *ddc; + struct pci_dev *pdev; + struct device *dev; + struct dc *dc; + + 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(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + ctx->adev = drm_to_adev(ctx->drm); + ctx->adev->dev = dev; + ctx->adev->mode_info.num_crtc = 1; + dm_test_create_mode_props(test, ctx->adev); + + pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, pdev); + device_initialize(&pdev->dev); + pdev->dev.parent = dev; + pdev->dev.release = dm_test_conn_init_release_dev; + KUNIT_ASSERT_EQ(test, dev_set_name(&pdev->dev, "dm-test-i2c-parent"), 0); + KUNIT_ASSERT_EQ(test, device_add(&pdev->dev), 0); + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, dm_test_conn_init_unregister_dev, + &pdev->dev), 0); + ctx->adev->pdev = pdev; + + ctx->dm = &ctx->adev->dm; + ctx->dm->adev = ctx->adev; + ctx->dm->ddev = ctx->drm; + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc_ctx); + dc_ctx->driver_context = ctx->adev; + + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + link_srv->dp_get_encoding_format = dm_test_gm_enc_8b10b; + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + dc->ctx = dc_ctx; + dc->link_srv = link_srv; + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->link->connector_signal = signal; + ctx->link->dc = dc; + ctx->link->link_enc = kunit_kzalloc(test, sizeof(*ctx->link->link_enc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link->link_enc); + + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ddc); + ddc->ctx = dc_ctx; + ddc->link = ctx->link; + ctx->link->ddc = ddc; + + dc->links[0] = ctx->link; + dc->link_count = 1; + ctx->dm->dc = dc; + + /* amdgpu_dm_connector_destroy() and amdgpu_dm_encoder_destroy() kfree() these. */ + ctx->aconnector = kzalloc_obj(*ctx->aconnector); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + ctx->aencoder = kzalloc_obj(*ctx->aencoder); + KUNIT_ASSERT_NOT_NULL(test, ctx->aencoder); + KUNIT_ASSERT_EQ(test, amdgpu_dm_encoder_init(ctx->drm, ctx->aencoder, 0), 0); + + return ctx; +} + +/** + * dm_test_conn_init_hdmi - Test a HDMI connector is fully brought up + * @test: The KUnit test context + * + * The DC link is bound to the connector, an i2c adapter is created and + * registered as the DDC bus, the DRM connector is initialized with the DM + * funcs and helpers, the encoder is attached and the CEC notifier registered. + */ +static void dm_test_conn_init_hdmi(struct kunit *test) +{ + struct dm_test_conn_init_ctx *ctx = + dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_HDMI_TYPE_A); + struct drm_connector *connector = &ctx->aconnector->base; + + KUNIT_ASSERT_EQ(test, + amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0, + ctx->aencoder), 0); + + KUNIT_EXPECT_PTR_EQ(test, ctx->link->priv, ctx->aconnector); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector->i2c); + KUNIT_EXPECT_PTR_EQ(test, connector->ddc, &ctx->aconnector->i2c->base); + KUNIT_EXPECT_EQ(test, connector->connector_type, DRM_MODE_CONNECTOR_HDMIA); + KUNIT_EXPECT_NOT_NULL(test, connector->helper_private); + KUNIT_EXPECT_PTR_EQ(test, ctx->aconnector->dc_link, ctx->link); + KUNIT_EXPECT_EQ(test, ctx->aconnector->connector_id, 0); + KUNIT_EXPECT_EQ(test, connector->possible_encoders, + drm_encoder_mask(&ctx->aencoder->base)); + KUNIT_EXPECT_NOT_NULL(test, ctx->aconnector->notifier); +} + +/** + * dm_test_conn_init_dvi - Test a DVI link maps to a DVI-D connector + * @test: The KUnit test context + * + * A single link DVI signal is not HDMI, so no CEC notifier is registered. + */ +static void dm_test_conn_init_dvi(struct kunit *test) +{ + struct dm_test_conn_init_ctx *ctx = + dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_DVI_SINGLE_LINK); + + KUNIT_ASSERT_EQ(test, + amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0, + ctx->aencoder), 0); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->base.connector_type, + DRM_MODE_CONNECTOR_DVID); + KUNIT_EXPECT_NULL(test, ctx->aconnector->notifier); +} + +/** + * dm_test_conn_init_get_modes_hook - Test the installed get_modes helper + * @test: The KUnit test context + * + * The connector helper funcs that amdgpu_dm_connector_init() installs forward + * mode enumeration to amdgpu_dm_connector_get_modes(), which synthesizes the + * no-EDID fallback modes. + */ +static void dm_test_conn_init_get_modes_hook(struct kunit *test) +{ + struct dm_test_conn_init_ctx *ctx = + dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_HDMI_TYPE_A); + const struct drm_connector_helper_funcs *helper; + struct drm_connector *connector = &ctx->aconnector->base; + + KUNIT_ASSERT_EQ(test, + amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0, + ctx->aencoder), 0); + + helper = connector->helper_private; + KUNIT_ASSERT_NOT_NULL(test, helper); + KUNIT_ASSERT_NOT_NULL(test, helper->get_modes); + + KUNIT_EXPECT_GT(test, helper->get_modes(connector), 0); + KUNIT_EXPECT_GT(test, ctx->aconnector->num_modes, 0); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -9839,6 +10025,10 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_fs_caps_force_min_hz_quirk), KUNIT_CASE(dm_test_fs_caps_mccs_clears_capability), KUNIT_CASE(dm_test_fs_caps_disables_replay), + /* amdgpu_dm_connector_init */ + KUNIT_CASE(dm_test_conn_init_hdmi), + KUNIT_CASE(dm_test_conn_init_dvi), + KUNIT_CASE(dm_test_conn_init_get_modes_hook), {} }; From 910e2348951c41a67fc0849e894702609e0fa190 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 15:19:56 -0600 Subject: [PATCH 013/114] drm/amd/display: Test forced atomic commit [WHAT] Add KUnit tests for dm_force_atomic_commit() covering a successful restore of the connector, its CRTC and the CRTC primary plane, and the error path taken when the connector state cannot be duplicated. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_connector.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_connector.h | 1 + .../tests/amdgpu_dm_connector_test.c | 131 ++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 5e95c8d5bb46..eba77a6f50e2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -3507,7 +3507,7 @@ out_free: } EXPORT_IF_KUNIT(amdgpu_dm_connector_init); -static int dm_force_atomic_commit(struct drm_connector *connector) +STATIC_IFN_KUNIT int dm_force_atomic_commit(struct drm_connector *connector) { int ret = 0; struct drm_device *ddev = connector->dev; @@ -3567,6 +3567,7 @@ out: return ret; } +EXPORT_IF_KUNIT(dm_force_atomic_commit); /* * This function handles all cases when set mode does not come upon hotplug. diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h index fb3f3d8a5283..79698deacf61 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h @@ -226,5 +226,6 @@ void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector); struct drm_atomic_commit; int amdgpu_dm_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_commit *state); +int dm_force_atomic_commit(struct drm_connector *connector); #endif #endif /* __AMDGPU_DM_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index d03e0a67c42a..8117aaf41f22 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -9620,6 +9620,134 @@ static void dm_test_conn_init_get_modes_hook(struct kunit *test) KUNIT_EXPECT_GT(test, ctx->aconnector->num_modes, 0); } +/* Tests for dm_force_atomic_commit() */ + +/* Fails the very first state duplication so the error path is taken. */ +static struct drm_connector_state * +dm_test_force_dup_state_fail(struct drm_connector *connector) +{ + return NULL; +} + +static const struct drm_connector_funcs dm_test_force_fail_funcs = { + .reset = amdgpu_dm_connector_funcs_reset, + .atomic_duplicate_state = dm_test_force_dup_state_fail, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* The atomic modeset check dereferences the helper funcs unconditionally. */ +static int dm_test_force_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static const struct drm_connector_helper_funcs dm_test_force_conn_helper_funcs = { + .get_modes = dm_test_force_get_modes, +}; + +struct dm_test_force_ctx { + struct drm_device *drm; + struct amdgpu_dm_connector *aconnector; + struct drm_modeset_acquire_ctx acquire_ctx; +}; + +static void dm_test_force_drop_locks(void *data) +{ + struct dm_test_force_ctx *ctx = data; + + ctx->drm->mode_config.acquire_ctx = NULL; + drm_modeset_drop_locks(&ctx->acquire_ctx); + drm_modeset_acquire_fini(&ctx->acquire_ctx); +} + +/* + * Build a disabled but fully initialized pipe: a primary plane, a CRTC (whose + * drm_crtc is the first member of amdgpu_crtc, so to_amdgpu_crtc() resolves) + * and a connector wired to an encoder that still points at the CRTC, which is + * the state dm_force_atomic_commit() is asked to restore. + */ +static struct dm_test_force_ctx * +dm_test_force_ctx_alloc(struct kunit *test, const struct drm_connector_funcs *funcs) +{ + struct dm_test_force_ctx *ctx; + struct drm_encoder *encoder; + struct drm_plane *primary; + struct drm_crtc *crtc; + 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(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + + primary = drm_kunit_helper_create_primary_plane(test, ctx->drm, NULL, NULL, + NULL, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + crtc = drm_kunit_helper_create_crtc(test, ctx->drm, primary, NULL, NULL, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + + ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + KUNIT_ASSERT_EQ(test, + drmm_connector_init(ctx->drm, &ctx->aconnector->base, funcs, + DRM_MODE_CONNECTOR_HDMIA, NULL), 0); + drm_connector_helper_add(&ctx->aconnector->base, &dm_test_force_conn_helper_funcs); + + encoder = drmm_kzalloc(ctx->drm, sizeof(*encoder), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, encoder); + KUNIT_ASSERT_EQ(test, + drmm_encoder_init(ctx->drm, encoder, NULL, + DRM_MODE_ENCODER_TMDS, NULL), 0); + encoder->crtc = crtc; + ctx->aconnector->base.encoder = encoder; + + drm_mode_config_reset(ctx->drm); + + drm_modeset_acquire_init(&ctx->acquire_ctx, 0); + ctx->drm->mode_config.acquire_ctx = &ctx->acquire_ctx; + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, dm_test_force_drop_locks, ctx), 0); + + return ctx; +} + +/** + * dm_test_force_commit_succeeds - Test the forced restore commits the pipe + * @test: The KUnit test context + * + * The connector, its CRTC and the CRTC primary plane are pulled into a fresh + * atomic state, the CRTC is marked as needing a modeset and the commit + * succeeds. + */ +static void dm_test_force_commit_succeeds(struct kunit *test) +{ + struct dm_test_force_ctx *ctx = + dm_test_force_ctx_alloc(test, &dm_test_connector_funcs); + + KUNIT_EXPECT_EQ(test, dm_force_atomic_commit(&ctx->aconnector->base), 0); +} + +/** + * dm_test_force_commit_conn_state_error - Test a failed connector state duplication + * @test: The KUnit test context + * + * When the connector state cannot be duplicated the error is propagated and + * the partially built atomic state is released without committing. + */ +static void dm_test_force_commit_conn_state_error(struct kunit *test) +{ + struct dm_test_force_ctx *ctx = + dm_test_force_ctx_alloc(test, &dm_test_force_fail_funcs); + + KUNIT_EXPECT_EQ(test, dm_force_atomic_commit(&ctx->aconnector->base), -ENOMEM); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -10029,6 +10157,9 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_conn_init_hdmi), KUNIT_CASE(dm_test_conn_init_dvi), KUNIT_CASE(dm_test_conn_init_get_modes_hook), + /* dm_force_atomic_commit */ + KUNIT_CASE(dm_test_force_commit_succeeds), + KUNIT_CASE(dm_test_force_commit_conn_state_error), {} }; From 80065ab5bcf2546669113060a5ce51402784323f Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:38:06 -0600 Subject: [PATCH 014/114] drm/amd/display: Test DCC reject for multi-plane format [WHAT] Extend the amdgpu_dm_plane_format_mod_supported() test to cover the rejection of a format that carries an extra plane alongside a DCC modifier. [HOW] Reuse the existing GFX9 DCC modifier and ask for XRGB8888_A8, whose 4 bytes-per-pixel main plane passes the bpp check but whose alpha plane collides with the DCC metadata plane. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 5ae627bb55cc..4d8d431bba48 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -506,6 +506,11 @@ static void dm_test_format_mod_supported(struct kunit *test) KUNIT_EXPECT_FALSE(test, amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_NV12, listed_mod)); + + /* 4 bytes per pixel, but its extra plane collides with the DCC metadata plane. */ + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888_A8, + listed_mod)); } /** From 9e7ed95bfdd9f7a1b7d1f2b7cb5a81ce05b66876 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:38:18 -0600 Subject: [PATCH 015/114] drm/amd/display: Test modifier list growth failure [WHAT] Add a KUnit test for amdgpu_dm_plane_add_modifier() covering the allocation failure that happens while growing the modifier list. [HOW] Seed a capacity whose doubling overflows the kmalloc_array() size product, so the allocation fails without fault injection, and assert the list is released and cleared for the caller. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 4d8d431bba48..6d81074ad598 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3686,6 +3686,28 @@ static void dm_test_atomic_async_update_copies_state(struct kunit *test) KUNIT_EXPECT_EQ(test, cur_state->crtc_h, 64U); } +/** + * dm_test_add_modifier_alloc_failure() - Verify the growth allocation failure. + * @test: KUnit test context. + * + * Verify if a failed capacity growth releases and clears the modifier list so + * the caller can detect the allocation failure. + */ +static void dm_test_add_modifier_alloc_failure(struct kunit *test) +{ + /* Doubling this capacity overflows the kmalloc_array() size product. */ + u64 cap = 1ULL << 62; + u64 size = cap; + u64 *mods = kmalloc_array(1, sizeof(*mods), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, mods); + + amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0x1234ULL); + + KUNIT_EXPECT_PTR_EQ(test, mods, NULL); + KUNIT_EXPECT_EQ(test, size, 1ULL << 62); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -3782,6 +3804,7 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_add_modifier_appends_value), KUNIT_CASE(dm_test_add_modifier_grows_capacity), KUNIT_CASE(dm_test_add_modifier_noop_when_mods_null), + KUNIT_CASE(dm_test_add_modifier_alloc_failure), /* amdgpu_dm_plane_add_modifier_dedup() */ KUNIT_CASE(dm_test_add_modifier_dedup_skips_duplicate), KUNIT_CASE(dm_test_add_modifier_dedup_noop_when_mods_null), From 567aadce7e28a9f4b2c550d1a47f00841b71cee8 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:38:31 -0600 Subject: [PATCH 016/114] drm/amd/display: Test pre-GFX9 plane buffer attributes [WHAT] Add a KUnit test for amdgpu_dm_plane_fill_plane_buffer_attributes() covering the pre-Vega dispatch to the GFX6 modifier decoder. [HOW] Run a GFX6-family framebuffer through a 1D tiled modifier and check the decoded tiling version, then repeat with a GFX9 modifier to check that the decode error is propagated instead. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 6d81074ad598..65ead566f56a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3708,6 +3708,52 @@ static void dm_test_add_modifier_alloc_failure(struct kunit *test) KUNIT_EXPECT_EQ(test, size, 1ULL << 62); } +/** + * dm_test_fill_plane_buffer_attributes_gfx6() - Verify the pre-GFX9 dispatch. + * @test: KUnit test context. + * + * Verify if a pre-Vega family decodes tiling from the GFX6 modifier instead of + * taking the GFX9 or GFX12 modifier paths, and propagates the decode error for + * a modifier the GFX6 decoder does not understand. + */ +static void dm_test_fill_plane_buffer_attributes_gfx6(struct kunit *test) +{ + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + struct amdgpu_framebuffer *afb; + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, afb); + + adev->family = AMDGPU_FAMILY_CZ; + afb->address = 0x80000000ULL; + afb->base.width = 1920; + afb->base.height = 1080; + afb->base.pitches[0] = 7680; + afb->base.format = drm_format_info(DRM_FORMAT_XRGB8888); + KUNIT_ASSERT_NOT_NULL(test, afb->base.format); + afb->base.modifier = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX6) | + AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1) | + AMD_FMT_MOD_SET(MICROTILE, AMD_FMT_MOD_MICROTILE_DISPLAY); + + KUNIT_EXPECT_EQ(test, dm_test_graphics_attrs(adev, afb, &tiling_info, + &plane_size, &dcc, &address), 0); + KUNIT_EXPECT_EQ(test, address.type, (int)PLN_ADDR_TYPE_GRAPHICS); + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion8); + + afb->base.modifier = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9); + + KUNIT_EXPECT_EQ(test, dm_test_graphics_attrs(adev, afb, &tiling_info, + &plane_size, &dcc, &address), -EINVAL); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -3746,6 +3792,7 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_fill_plane_buffer_attributes() */ KUNIT_CASE(dm_test_fill_plane_buffer_attributes_video), KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx12), + KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx6), /* amdgpu_dm_plane_get_cursor_position() */ KUNIT_CASE(dm_test_get_cursor_position), KUNIT_CASE(dm_test_get_cursor_position_bad_size), From 128cf6682d2d3f2495e0a025798dc2d409fb521a Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:38:43 -0600 Subject: [PATCH 017/114] drm/amd/display: Test accepted plane atomic check [WHAT] Add a KUnit test for amdgpu_dm_plane_atomic_check() covering the success path taken once DC validation accepts the plane. [HOW] Extend the shared atomic-check fixture with a resource pool that has no validate_plane callback, so dc_validate_plane() only inspects the source and destination rectangles. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 65ead566f56a..405b8d0d7a94 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3754,6 +3754,41 @@ static void dm_test_fill_plane_buffer_attributes_gfx6(struct kunit *test) &plane_size, &dcc, &address), -EINVAL); } +/** + * dm_test_atomic_check_success() - Verify a fully valid plane state is accepted. + * @test: KUnit test context. + * + * Verify if atomic_check reports success once DC validation accepts the plane. + * The resource pool exposes no validate_plane callback, so DC falls back to + * checking the source and destination rectangles only. + */ +static void dm_test_atomic_check_success(struct kunit *test) +{ + struct dm_plane_state *dm_plane_state; + struct drm_crtc_state *new_crtc_state; + struct drm_atomic_commit *state; + struct resource_pool *res_pool; + struct amdgpu_device *adev; + struct drm_framebuffer *fb; + struct drm_plane *plane; + + adev = dm_test_init_atomic_check_state(test, &state, &plane, &dm_plane_state, + &new_crtc_state, &fb); + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, res_pool); + res_pool->funcs = kunit_kzalloc(test, sizeof(*res_pool->funcs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, res_pool->funcs); + + adev->dm.dc->res_pool = res_pool; + + dm_plane_state->dc_state->src_rect.width = 100; + dm_plane_state->dc_state->src_rect.height = 100; + dm_plane_state->dc_state->dst_rect.width = 100; + dm_plane_state->dc_state->dst_rect.height = 100; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), 0); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -3837,6 +3872,7 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_atomic_check_helper_failure), KUNIT_CASE(dm_test_atomic_check_color_pipeline_conflict), KUNIT_CASE(dm_test_atomic_check_scaling_failure), + KUNIT_CASE(dm_test_atomic_check_success), /* amdgpu_dm_plane_panic_flush() */ KUNIT_CASE(dm_test_panic_flush_no_dc_state), /* amdgpu_dm_plane_drm_plane_reset() */ From 7352d162d04011c26b67b095a0f0d0b02d98d3f7 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:38:55 -0600 Subject: [PATCH 018/114] drm/amd/display: Test cursor update without DC stream [WHAT] Add KUnit tests for amdgpu_dm_plane_handle_cursor_update() covering a cursor reported as disabled, and an enabled cursor whose attributes are assembled before DC programming. [HOW] Add a fixture binding a 64x64 cursor plane to an amdgpu_crtc whose DM CRTC state carries no stream. Place the cursor off the left edge for the disabled case, and advertise the degamma ROM capability for the enabled case. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 405b8d0d7a94..2e66099cc0f8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3789,6 +3789,102 @@ static void dm_test_atomic_check_success(struct kunit *test) KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), 0); } +struct dm_test_cursor_ctx { + struct amdgpu_device *adev; + struct amdgpu_crtc *acrtc; + struct dm_crtc_state *crtc_state; + struct drm_plane_state *state; + struct drm_plane_state old_state; + struct drm_plane *plane; +}; + +/* + * Build a 64x64 cursor plane bound to an amdgpu_crtc whose DM CRTC state + * carries no DC stream, so the cursor update stops before programming DC. + */ +static struct dm_test_cursor_ctx *dm_test_alloc_cursor_ctx(struct kunit *test) +{ + struct amdgpu_framebuffer *afb; + struct dm_test_cursor_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, afb); + + ctx->adev = dm_kunit_alloc_adev(test); + ctx->acrtc = kunit_kzalloc(test, sizeof(*ctx->acrtc), GFP_KERNEL); + ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL); + ctx->state = kunit_kzalloc(test, sizeof(*ctx->state), GFP_KERNEL); + ctx->plane = kunit_kzalloc(test, sizeof(*ctx->plane), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->acrtc); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state); + KUNIT_ASSERT_NOT_NULL(test, ctx->state); + KUNIT_ASSERT_NOT_NULL(test, ctx->plane); + + ctx->adev->dm.dc = dm_kunit_alloc_dc_with_ctx(test); + + ctx->acrtc->base.dev = &ctx->adev->ddev; + ctx->acrtc->base.state = &ctx->crtc_state->base; + ctx->acrtc->max_cursor_width = 64; + ctx->acrtc->max_cursor_height = 64; + + afb->address = 0x80000000ULL; + afb->base.pitches[0] = 256; + afb->base.format = drm_format_info(DRM_FORMAT_ARGB8888); + KUNIT_ASSERT_NOT_NULL(test, afb->base.format); + + ctx->state->fb = &afb->base; + ctx->state->crtc = &ctx->acrtc->base; + ctx->state->crtc_w = 64; + ctx->state->crtc_h = 64; + + ctx->plane->dev = &ctx->adev->ddev; + ctx->plane->state = ctx->state; + + return ctx; +} + +/** + * dm_test_handle_cursor_update_disabled() - Verify the cursor-off path. + * @test: KUnit test context. + * + * Verify if a cursor positioned fully off the left edge is treated as disabled + * and returns before the cursor geometry is latched on the CRTC. + */ +static void dm_test_handle_cursor_update_disabled(struct kunit *test) +{ + struct dm_test_cursor_ctx *ctx = dm_test_alloc_cursor_ctx(test); + + ctx->state->crtc_x = -64; + + amdgpu_dm_plane_handle_cursor_update(ctx->plane, &ctx->old_state); + + KUNIT_EXPECT_EQ(test, ctx->acrtc->cursor_width, 0); + KUNIT_EXPECT_EQ(test, ctx->acrtc->cursor_height, 0); +} + +/** + * dm_test_handle_cursor_update_no_stream() - Verify cursor attribute assembly. + * @test: KUnit test context. + * + * Verify if an enabled cursor latches its geometry on the CRTC and builds the + * DC cursor attributes, including the degamma ROM bit, before stopping at the + * missing DC stream. + */ +static void dm_test_handle_cursor_update_no_stream(struct kunit *test) +{ + struct dm_test_cursor_ctx *ctx = dm_test_alloc_cursor_ctx(test); + + ctx->crtc_state->cm_is_degamma_srgb = true; + ctx->adev->dm.dc->caps.color.dpp.gamma_corr = true; + + amdgpu_dm_plane_handle_cursor_update(ctx->plane, &ctx->old_state); + + KUNIT_EXPECT_EQ(test, ctx->acrtc->cursor_width, 64); + KUNIT_EXPECT_EQ(test, ctx->acrtc->cursor_height, 64); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -3861,6 +3957,8 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_helper_cleanup_fb_no_fb), /* amdgpu_dm_plane_handle_cursor_update() */ KUNIT_CASE(dm_test_handle_cursor_update_no_fb), + KUNIT_CASE(dm_test_handle_cursor_update_disabled), + KUNIT_CASE(dm_test_handle_cursor_update_no_stream), /* amdgpu_dm_plane_atomic_async_update() */ KUNIT_CASE(dm_test_atomic_async_update_copies_state), /* amdgpu_dm_plane_atomic_async_check() */ From 48c78d2199c0fb922e52b8f2b0d9f4574cd7c833 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:39:07 -0600 Subject: [PATCH 019/114] drm/amd/display: Test panic flush DCC teardown [WHAT] Add a KUnit test for amdgpu_dm_plane_panic_flush() covering the request to clear DCC and tiling on the active pipe. [HOW] Install a spy on dc->hwss.clear_surface_dcc_and_tiling and drive the flush with a non-linear framebuffer, counting the calls that ask for the tiling teardown. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 2e66099cc0f8..01275baa7589 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3885,6 +3885,62 @@ static void dm_test_handle_cursor_update_no_stream(struct kunit *test) KUNIT_EXPECT_EQ(test, ctx->acrtc->cursor_height, 64); } +static int dm_test_clear_dcc_tiling_count; + +static void dm_test_clear_surface_dcc_and_tiling(struct pipe_ctx *pipe_ctx, + struct dc_plane_state *plane_state, + bool clear_tiling) +{ + if (clear_tiling) + dm_test_clear_dcc_tiling_count++; +} + +/** + * dm_test_panic_flush_disables_dcc() - Verify panic flush disables DCC. + * @test: KUnit test context. + * + * Verify if panic_flush asks DC to clear DCC on the active pipe, and requests + * the tiling teardown as well when the framebuffer is not linear. + */ +static void dm_test_panic_flush_disables_dcc(struct kunit *test) +{ + struct dc_plane_state *dc_plane_state; + struct dm_plane_state *dm_plane_state; + struct resource_pool *res_pool; + struct drm_framebuffer *fb; + struct drm_plane *plane; + struct dc *dc; + + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL); + dc_plane_state = kunit_kzalloc(test, sizeof(*dc_plane_state), GFP_KERNEL); + fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, plane); + KUNIT_ASSERT_NOT_NULL(test, dm_plane_state); + KUNIT_ASSERT_NOT_NULL(test, dc_plane_state); + KUNIT_ASSERT_NOT_NULL(test, fb); + KUNIT_ASSERT_NOT_NULL(test, res_pool); + + dc = dm_kunit_alloc_dc_with_ctx(test); + dc->current_state = dm_kunit_alloc_dc_state(test); + dc->hwss.clear_surface_dcc_and_tiling = dm_test_clear_surface_dcc_and_tiling; + res_pool->pipe_count = 1; + dc->res_pool = res_pool; + dc_plane_state->ctx = dc->ctx; + + fb->modifier = AMD_FMT_MOD; + dm_plane_state->base.fb = fb; + dm_plane_state->dc_state = dc_plane_state; + plane->state = &dm_plane_state->base; + + dm_test_clear_dcc_tiling_count = 0; + + amdgpu_dm_plane_panic_flush(plane); + + KUNIT_EXPECT_EQ(test, dm_test_clear_dcc_tiling_count, 1); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -3973,6 +4029,7 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_atomic_check_success), /* amdgpu_dm_plane_panic_flush() */ KUNIT_CASE(dm_test_panic_flush_no_dc_state), + KUNIT_CASE(dm_test_panic_flush_disables_dcc), /* amdgpu_dm_plane_drm_plane_reset() */ KUNIT_CASE(dm_test_plane_reset_initializes_state), /* amdgpu_dm_plane_drm_plane_duplicate_state() */ From 055593492a1f72c768c1ef5698e0b8dddf128d73 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 29 Aug 2026 13:39:20 -0600 Subject: [PATCH 020/114] drm/amd/display: Test optional plane property creation [WHAT] Add KUnit tests for amdgpu_dm_plane_init() covering the rotation property and framebuffer damage clips on newer ASICs, and the default color pipeline on DCN 3.0 and newer. [HOW] Initialize an overlay plane on a standard DRM KUnit device, varying the ASIC type, the DCE IP version and the DC context DCE version to select each optional property. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 1 + .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 91994fe37cbf..4c13c7e86091 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -2319,6 +2319,7 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_init); bool amdgpu_dm_plane_is_video_format(uint32_t format) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 01275baa7589..2d2397e9135c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -3941,6 +3941,73 @@ static void dm_test_panic_flush_disables_dcc(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_test_clear_dcc_tiling_count, 1); } +/* + * Build a DM ready to initialize planes on a real DRM device. The plane itself + * is plain kzalloc'd because DRM frees it through drm_plane_helper_destroy() + * when the mode config is torn down. + */ +static struct drm_plane *dm_test_alloc_init_plane(struct kunit *test, + struct amdgpu_device **adev_out) +{ + struct amdgpu_device *adev; + struct drm_plane *plane; + + adev = dm_kunit_alloc_adev(test); + adev->family = AMDGPU_FAMILY_NV; + adev->dm.adev = adev; + adev->dm.dc = dm_kunit_alloc_dc_with_ctx(test); + + plane = kzalloc_obj(*plane, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, plane); + plane->type = DRM_PLANE_TYPE_OVERLAY; + + *adev_out = adev; + + return plane; +} + +/** + * dm_test_plane_init_creates_optional_properties() - Verify optional properties. + * @test: KUnit test context. + * + * Verify if a Bonaire-or-newer non-cursor plane on a DCE version above 3.0.1 + * gains the rotation property and framebuffer damage clips. + */ +static void dm_test_plane_init_creates_optional_properties(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_plane *plane; + + plane = dm_test_alloc_init_plane(test, &adev); + adev->asic_type = CHIP_BONAIRE; + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 0); + + KUNIT_ASSERT_EQ(test, amdgpu_dm_plane_init(&adev->dm, plane, 1, NULL), 0); + + KUNIT_EXPECT_NOT_NULL(test, plane->rotation_property); + KUNIT_EXPECT_NOT_NULL(test, adev->ddev.mode_config.prop_fb_damage_clips); +} + +/** + * dm_test_plane_init_creates_color_pipeline() - Verify color pipeline creation. + * @test: KUnit test context. + * + * Verify if a non-cursor plane on DCN 3.0 or newer gets a default color + * pipeline exposed through the COLOR_PIPELINE property. + */ +static void dm_test_plane_init_creates_color_pipeline(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_plane *plane; + + plane = dm_test_alloc_init_plane(test, &adev); + adev->dm.dc->ctx->dce_version = DCN_VERSION_3_0; + + KUNIT_ASSERT_EQ(test, amdgpu_dm_plane_init(&adev->dm, plane, 1, NULL), 0); + + KUNIT_EXPECT_NOT_NULL(test, plane->color_pipeline_property); +} + static struct kunit_case amdgpu_dm_plane_test_cases[] = { /* amdgpu_dm_plane_is_video_format() */ KUNIT_CASE(dm_test_plane_is_video_format_known_video), @@ -4078,6 +4145,9 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_validate_dcc_not_capable_fails), KUNIT_CASE(dm_test_validate_dcc_success_and_scan_mapping), KUNIT_CASE(dm_test_validate_dcc_independent_64b_mismatch_fails), + /* amdgpu_dm_plane_init() */ + KUNIT_CASE(dm_test_plane_init_creates_optional_properties), + KUNIT_CASE(dm_test_plane_init_creates_color_pipeline), {} }; From 8ff6a6ef99a6ab2f25933cfb5bf1d0d3d7d267d8 Mon Sep 17 00:00:00 2001 From: Yi-Ling Chen Date: Tue, 1 Sep 2026 15:33:36 +0800 Subject: [PATCH 021/114] drm/amd/display: Add option for certain panels to disable FEC [Why] Disable FEC for certain panels. [How] To align FEC HW/SW status when enabling the option. Reviewed-by: Aric Cyr Signed-off-by: Yi-Ling Chen Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index dbdaf9415dfa..171d8bb24844 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1869,6 +1869,12 @@ bool dc_validate_boot_timing(const struct dc *dc, return false; } + if (sink->edid_caps.panel_patch.disable_fec && + (link->fec_state == dc_link_fec_enabled)) { + DC_LOG_DEBUG("boot timing validation failed due to FEC WA & FEC HW is active\n"); + return false; + } + /* Check for enabled DIG to identify enabled display */ if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) { DC_LOG_DEBUG("boot timing validation failed due to disabled DIG\n"); From b22e211fa4a627d6c8f49abb70b4e2d79865ffbd Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:03:44 -0600 Subject: [PATCH 022/114] drm/amd/display: Build MST DSC helpers for KUnit [WHAT] The MST DSC helpers in amdgpu_dm_mst_types.c are compiled only when CONFIG_DRM_AMD_DC_FP is enabled, which no UML KUnit build can select, so none of them can be reached from the KUnit suite. [HOW] Widen the CONFIG_DRM_AMD_DC_FP guards to also build when CONFIG_DRM_AMD_DC_KUNIT_TEST is set. The only caller of validate_dsc_caps_on_connector() is guarded as well, so guard it by IS_ENABLED() instead to keep it out of the KUnit build while still referencing the helper. With the real bodies now built, dm_mst_test_fp_guarded_public_stubs() no longer describes reachable behaviour, so remove it along with its case entry. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 12 ++++++------ .../tests/amdgpu_dm_mst_types_test.c | 19 ------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index bce605fd6cdf..aad8fff2cd6f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -278,7 +278,7 @@ bool needs_dsc_aux_workaround(struct dc_link *link) } EXPORT_IF_KUNIT(needs_dsc_aux_workaround); -#if defined(CONFIG_DRM_AMD_DC_FP) +#if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) static bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port) { u8 branch_vendor_data[4] = { 0 }; // Vendor data 0x50C ~ 0x50F @@ -504,8 +504,8 @@ STATIC_IFN_KUNIT int dm_dp_mst_get_modes(struct drm_connector *connector) amdgpu_dm_update_freesync_caps( connector, aconnector->drm_edid, true); -#if defined(CONFIG_DRM_AMD_DC_FP) - if (!validate_dsc_caps_on_connector(aconnector)) +#if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) + if (IS_ENABLED(CONFIG_DRM_AMD_DC_FP) && !validate_dsc_caps_on_connector(aconnector)) memset(&aconnector->dc_sink->dsc_caps, 0, sizeof(aconnector->dc_sink->dsc_caps)); #endif @@ -937,7 +937,7 @@ struct dsc_mst_fairness_params { struct amdgpu_dm_connector *aconnector; }; -#if defined(CONFIG_DRM_AMD_DC_FP) +#if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) static uint16_t get_fec_overhead_multiplier(struct dc_link *dc_link) { u8 link_coding_cap; @@ -1928,7 +1928,7 @@ static bool is_dsc_common_config_possible(struct dc_stream_state *stream, } #endif -#if defined(CONFIG_DRM_AMD_DC_FP) +#if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) static bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw) { uint32_t total_data_bw_efficiency_x10000 = 0; @@ -1990,7 +1990,7 @@ enum dc_status dm_dp_mst_is_port_support_mode( struct amdgpu_dm_connector *aconnector, struct dc_stream_state *stream) { -#if defined(CONFIG_DRM_AMD_DC_FP) +#if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) int branch_max_throughput_mps = 0; struct dc_link_settings cur_link_settings; uint32_t end_to_end_bw_in_kbps = 0; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 064af0022fb6..36d3f0eebb50 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -2058,21 +2058,6 @@ static void dm_mst_test_sideband_msg_ready_ack_write_fails(struct kunit *test) dm_mst_test_free_armed_sideband_connector(aconnector); } -#if !defined(CONFIG_DRM_AMD_DC_FP) -/** - * dm_mst_test_fp_guarded_public_stubs - Test FP-off public fallbacks - * @test: KUnit test context - * - * When CONFIG_DRM_AMD_DC_FP is disabled, the public DSC validation helper - * has no FP body and must return DC_OK without touching its arguments. - */ -static void dm_mst_test_fp_guarded_public_stubs(struct kunit *test) -{ - KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(NULL, NULL), - (enum dc_status)DC_OK); -} -#endif - static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -2152,10 +2137,6 @@ static struct kunit_case dm_mst_types_test_cases[] = { /* dm_dp_mst_connector_destroy tests */ KUNIT_CASE(dm_mst_test_connector_destroy_no_sink), KUNIT_CASE(dm_mst_test_connector_destroy_releases_sink), - /* CONFIG_DRM_AMD_DC_FP disabled public paths */ -#if !defined(CONFIG_DRM_AMD_DC_FP) - KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs), -#endif {} }; From cc8755b73897101c361c13a5140c3ba5c85b3cfa Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:03:57 -0600 Subject: [PATCH 023/114] drm/amd/display: Test oversized AUX transfer [WHAT] Add a KUnit test for dm_dp_aux_transfer() covering the message size rejection path. [HOW] Build a drm_dp_aux_msg whose size exceeds the aux_payload buffer and verify that dm_dp_aux_transfer() returns -E2BIG without issuing a transfer. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../tests/amdgpu_dm_mst_types_test.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 36d3f0eebb50..af2dd5b8e79d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -885,6 +885,26 @@ static void dm_mst_test_aux_transfer_non_ack_reply(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U); } +/** + * dm_mst_test_aux_transfer_oversized - oversized AUX message is rejected. + * @test: KUnit test context. + * + * The payload is copied into a 16-byte stack buffer, so a larger message must + * be rejected with -E2BIG before the AUX handle is touched. The guard warns, + * which is the expected behaviour for this caller bug. + */ +static void dm_mst_test_aux_transfer_oversized(struct kunit *test) +{ + struct drm_dp_aux_msg msg = { 0 }; + u8 buffer[17] = { 0 }; + + msg.request = DP_AUX_NATIVE_WRITE; + msg.buffer = buffer; + msg.size = sizeof(buffer); + + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer(NULL, &msg), (ssize_t)-E2BIG); +} + /** * dm_mst_test_fill_payload_flags_native_write - native write request decode. * @test: KUnit test context. @@ -2089,6 +2109,7 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_aux_transfer_error_result), KUNIT_CASE(dm_mst_test_aux_transfer_hpd_discon_quirk), KUNIT_CASE(dm_mst_test_aux_transfer_non_ack_reply), + KUNIT_CASE(dm_mst_test_aux_transfer_oversized), /* dm_dp_aux_fill_payload_flags tests */ KUNIT_CASE(dm_mst_test_fill_payload_flags_native_write), KUNIT_CASE(dm_mst_test_fill_payload_flags_native_read), From c0919827569ead41028f04ce665c4f6d3085c18f Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:04:11 -0600 Subject: [PATCH 024/114] drm/amd/display: Test MST connector creation [WHAT] Add KUnit tests for dm_dp_add_mst_connector() covering successful connector creation, inheritance of the master connector properties and the connector init failure path. [HOW] Set up an MST topology manager owned by a master amdgpu_dm_connector plus the mode properties the helper attaches, then add a port and check the resulting connector, its property values and the encoder links. The failure path is driven by a mode config without the connector functions required by drm_connector_init(), and the port reference count is checked to confirm the error unwind releases it. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 4 + .../tests/amdgpu_dm_mst_types_test.c | 202 ++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index aad8fff2cd6f..f857303fe386 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -660,7 +660,7 @@ dm_dp_create_fake_mst_encoders(struct amdgpu_device *adev) } EXPORT_IF_KUNIT(dm_dp_create_fake_mst_encoders); -static struct drm_connector * +STATIC_IFN_KUNIT struct drm_connector * dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *pathprop) @@ -742,6 +742,7 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, return connector; } +EXPORT_IF_KUNIT(dm_dp_add_mst_connector); /* * Select the ESI[1] mask used to filter the MST sideband ready bits for a diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 50dca0ef98fe..0e6189219eb2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -65,6 +65,7 @@ struct dc_state; struct dc_stream_state; struct dm_atomic_state; struct drm_atomic_commit; +struct drm_dp_mst_port; struct drm_dp_mst_topology_mgr; uint32_t dm_mst_get_pbn_divider(struct dc_link *link); @@ -119,6 +120,9 @@ int dm_dp_mst_atomic_check(struct drm_connector *connector, int dm_dp_mst_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force); int dm_dp_mst_get_modes(struct drm_connector *connector); +struct drm_connector *dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + const char *pathprop); int amdgpu_dm_mst_connector_late_register(struct drm_connector *connector); void amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector); void dm_dp_mst_connector_destroy(struct drm_connector *connector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index af2dd5b8e79d..f2bae3a50941 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1297,6 +1298,203 @@ static void dm_mst_test_create_fake_mst_encoders(struct kunit *test) } } +/* Tests for dm_dp_add_mst_connector */ + +struct dm_mst_test_add_ctx { + struct amdgpu_device *adev; + struct amdgpu_dm_connector *master; + struct drm_dp_mst_port *port; + struct dc_link *link; +}; + +/* + * Stand in for amdgpu_display_modeset_create_props(), which the test module + * cannot link against. Only the properties amdgpu_dm_connector_init_helper() + * attaches are created. + */ +static void dm_mst_test_create_mode_props(struct kunit *test, struct amdgpu_device *adev) +{ + struct amdgpu_mode_info *mode_info = &adev->mode_info; + struct drm_device *drm = adev_to_drm(adev); + + KUNIT_ASSERT_EQ(test, drm_mode_create_scaling_mode_property(drm), 0); + + mode_info->underscan_property = + drm_property_create_range(drm, 0, "underscan", 0, 1); + mode_info->underscan_hborder_property = + drm_property_create_range(drm, 0, "underscan hborder", 0, 128); + mode_info->underscan_vborder_property = + drm_property_create_range(drm, 0, "underscan vborder", 0, 128); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_property); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_hborder_property); + KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_vborder_property); +} + +/* + * Build the MST root connector and the topology port the new downstream + * connector hangs off. The port has no parent branch so the callback takes the + * "no branch descriptor" path without needing a sideband AUX backend. + */ +static void dm_mst_test_init_add_ctx(struct kunit *test, struct dm_mst_test_add_ctx *ctx) +{ + struct amdgpu_device *adev; + struct drm_device *drm; + int ret; + + adev = dm_kunit_alloc_adev(test); + drm = &adev->ddev; + ret = drmm_mode_config_init(drm); + KUNIT_ASSERT_EQ(test, ret, 0); + dm_mst_test_create_mode_props(test, adev); + + adev->dm.adev = adev; + adev->dm.ddev = drm; + adev->dm.display_indexes_num = 2; + adev->mode_info.num_crtc = 2; + dm_dp_create_fake_mst_encoders(adev); + + ctx->adev = adev; + ctx->link = dm_kunit_alloc_link(test); + /* Inflexible DIG mapping lets link_enc_cfg_get_link_enc() skip the dc lookup. */ + ctx->link->link_enc = kunit_kzalloc(test, sizeof(*ctx->link->link_enc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link->link_enc); + + ctx->master = dm_kunit_alloc_connector(test, adev, ctx->link); + ctx->master->connector_id = 5; + ret = drm_connector_init(drm, &ctx->master->base, &dm_mst_test_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx->port = kunit_kzalloc(test, sizeof(*ctx->port), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->port); + kref_init(&ctx->port->malloc_kref); + ctx->port->mgr = &ctx->master->mst_mgr; +} + +/* True when @prop is attached to the connector's mode object. */ +static bool dm_mst_test_has_prop(struct drm_connector *connector, struct drm_property *prop) +{ + struct drm_object_properties *props = connector->base.properties; + int i; + + if (!prop) + return false; + + for (i = 0; i < props->count; i++) + if (props->properties[i] == prop) + return true; + + return false; +} + +/** + * dm_mst_test_add_mst_connector_creates - Test downstream connector creation + * @test: KUnit test context + * + * The topology callback must allocate a DisplayPort connector bound to the port + * and the root connector, inherit the root's dc_link and connector id, attach + * every fake MST encoder, publish the path property and take a malloc + * reference on the port. + */ +static void dm_mst_test_add_mst_connector_creates(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct dm_mst_test_add_ctx ctx; + struct drm_connector *connector; + + dm_mst_test_init_add_ctx(test, &ctx); + + connector = dm_dp_add_mst_connector(&ctx.master->mst_mgr, ctx.port, "1-3"); + KUNIT_ASSERT_NOT_NULL(test, connector); + + aconnector = to_amdgpu_dm_connector(connector); + KUNIT_EXPECT_PTR_EQ(test, aconnector->mst_output_port, ctx.port); + KUNIT_EXPECT_PTR_EQ(test, aconnector->mst_root, ctx.master); + KUNIT_EXPECT_PTR_EQ(test, aconnector->dc_link, ctx.link); + KUNIT_EXPECT_EQ(test, aconnector->connector_id, 5); + KUNIT_EXPECT_TRUE(test, aconnector->mst_status & MST_PROBE); + KUNIT_EXPECT_EQ(test, aconnector->branch_ieee_oui, 0U); + KUNIT_EXPECT_EQ(test, connector->connector_type, DRM_MODE_CONNECTOR_DisplayPort); + KUNIT_EXPECT_EQ(test, connector->possible_encoders, 0x3U); + KUNIT_EXPECT_NOT_NULL(test, connector->state); + KUNIT_EXPECT_NOT_NULL(test, connector->path_blob_ptr); + KUNIT_EXPECT_EQ(test, kref_read(&ctx.port->malloc_kref), 2U); + KUNIT_EXPECT_TRUE(test, + dm_mst_test_has_prop(connector, + ctx.adev->ddev.mode_config.path_property)); + KUNIT_EXPECT_TRUE(test, + dm_mst_test_has_prop(connector, + ctx.adev->ddev.mode_config.tile_property)); + + dm_dp_mst_connector_destroy(connector); +} + +/** + * dm_mst_test_add_mst_connector_inherits_props - Test optional property sharing + * @test: KUnit test context + * + * Max bpc, VRR capable and colorspace properties are only attached to the new + * connector when the root connector already owns them, so the downstream + * connector shares the root's property objects. + */ +static void dm_mst_test_add_mst_connector_inherits_props(struct kunit *test) +{ + struct dm_mst_test_add_ctx ctx; + struct drm_connector *connector; + struct drm_device *drm; + + dm_mst_test_init_add_ctx(test, &ctx); + drm = &ctx.adev->ddev; + + ctx.master->base.max_bpc_property = + drm_property_create_range(drm, 0, "max bpc", 8, 16); + ctx.master->base.vrr_capable_property = + drm_property_create_bool(drm, DRM_MODE_PROP_IMMUTABLE, "vrr_capable"); + KUNIT_ASSERT_NOT_NULL(test, ctx.master->base.max_bpc_property); + KUNIT_ASSERT_NOT_NULL(test, ctx.master->base.vrr_capable_property); + KUNIT_ASSERT_EQ(test, drm_mode_create_dp_colorspace_property(&ctx.master->base, 0), 0); + + connector = dm_dp_add_mst_connector(&ctx.master->mst_mgr, ctx.port, "1-4"); + KUNIT_ASSERT_NOT_NULL(test, connector); + + KUNIT_EXPECT_PTR_EQ(test, connector->max_bpc_property, ctx.master->base.max_bpc_property); + KUNIT_EXPECT_PTR_EQ(test, connector->vrr_capable_property, + ctx.master->base.vrr_capable_property); + KUNIT_EXPECT_PTR_EQ(test, connector->colorspace_property, + ctx.master->base.colorspace_property); + KUNIT_EXPECT_TRUE(test, dm_mst_test_has_prop(connector, connector->max_bpc_property)); + KUNIT_EXPECT_TRUE(test, dm_mst_test_has_prop(connector, connector->colorspace_property)); + + dm_dp_mst_connector_destroy(connector); +} + +/** + * dm_mst_test_add_mst_connector_init_fails - Test the connector init failure path + * @test: KUnit test context + * + * Exhausting the device connector index space makes drm_connector_dynamic_init() + * fail, so the callback must free the connector it allocated, leave the port + * malloc reference untouched and report no connector to the MST helpers. + */ +static void dm_mst_test_add_mst_connector_init_fails(struct kunit *test) +{ + struct dm_mst_test_add_ctx ctx; + struct drm_device *drm; + int id; + + dm_mst_test_init_add_ctx(test, &ctx); + drm = &ctx.adev->ddev; + + /* drm_connector_init_only() takes an index out of the same [0, 31] space. */ + do { + id = ida_alloc_max(&drm->mode_config.connector_ida, 31, GFP_KERNEL); + } while (id >= 0); + KUNIT_ASSERT_EQ(test, id, -ENOSPC); + + KUNIT_EXPECT_NULL(test, dm_dp_add_mst_connector(&ctx.master->mst_mgr, ctx.port, "1-5")); + KUNIT_EXPECT_EQ(test, kref_read(&ctx.port->malloc_kref), 1U); +} + /** * dm_mst_test_atomic_check_no_old_crtc - Test atomic check no-op path * @test: KUnit test context @@ -2134,6 +2332,10 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_atomic_best_encoder), /* dm_dp_create_fake_mst_encoders tests */ KUNIT_CASE(dm_mst_test_create_fake_mst_encoders), + /* dm_dp_add_mst_connector tests */ + KUNIT_CASE(dm_mst_test_add_mst_connector_creates), + KUNIT_CASE(dm_mst_test_add_mst_connector_inherits_props), + KUNIT_CASE(dm_mst_test_add_mst_connector_init_fails), /* dm_dp_mst_atomic_check tests */ KUNIT_CASE(dm_mst_test_atomic_check_no_old_crtc), /* dm_dp_mst_detect tests */ From 46395b55cbcb0051efb2e16963293815874a8eac Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:04:24 -0600 Subject: [PATCH 025/114] drm/amd/display: Test link bandwidth readback [WHAT] Add KUnit tests for dp_get_link_current_set_bw() covering every link rate the helper knows about, the unknown rate fallback and the DPCD read error path. [HOW] Add a DPCD backing store for the DSC helpers that is addressed absolutely, because they read offsets spread across the whole map. A table driven test then programs the link rate and lane count into the fake DPCD and checks the returned bandwidth, with one entry arming the AUX transfer to fail. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 2 + .../tests/amdgpu_dm_mst_types_test.c | 120 ++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index f857303fe386..e8f4ca7e1f2a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1930,7 +1930,7 @@ static bool is_dsc_common_config_possible(struct dc_stream_state *stream, #endif #if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) -static bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw) +STATIC_IFN_KUNIT bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw) { uint32_t total_data_bw_efficiency_x10000 = 0; uint32_t link_rate_per_lane_kbps = 0; @@ -1985,6 +1985,7 @@ static bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_lin *cur_link_bw = link_rate_per_lane_kbps * lane_count.bits.LANE_COUNT_SET / 10000 * total_data_bw_efficiency_x10000; return true; } +EXPORT_IF_KUNIT(dp_get_link_current_set_bw); #endif enum dc_status dm_dp_mst_is_port_support_mode( diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 0e6189219eb2..4af3aac31223 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -65,6 +65,7 @@ struct dc_state; struct dc_stream_state; struct dm_atomic_state; struct drm_atomic_commit; +struct drm_dp_aux; struct drm_dp_mst_port; struct drm_dp_mst_topology_mgr; @@ -104,6 +105,7 @@ enum dc_status dm_dp_mst_is_port_support_mode( #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector); +bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); ssize_t dm_dp_aux_transfer_result(ssize_t result, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index f2bae3a50941..58a4bbb31e79 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -2276,6 +2276,124 @@ static void dm_mst_test_sideband_msg_ready_ack_write_fails(struct kunit *test) dm_mst_test_free_armed_sideband_connector(aconnector); } +/* + * Fake DPCD backing store for the DSC helpers. Unlike the sideband fake above + * it is addressed absolutely, because these helpers read offsets spread across + * the whole map (0x60 DSC caps, 0x100 link settings, 0x500 branch descriptor). + */ +#define DM_MST_TEST_DSC_DPCD_SIZE 0x600 + +static u8 dm_mst_test_dsc_dpcd[DM_MST_TEST_DSC_DPCD_SIZE]; +static struct drm_dp_aux *dm_mst_test_dsc_aux_fail; + +static ssize_t dm_mst_test_dsc_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + size_t i; + + if (aux == dm_mst_test_dsc_aux_fail) + return -EIO; + + if (msg->address + msg->size > DM_MST_TEST_DSC_DPCD_SIZE) + return -EINVAL; + + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + + if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE) + return msg->size; + + for (i = 0; i < msg->size; i++) + ((u8 *)msg->buffer)[i] = dm_mst_test_dsc_dpcd[msg->address + i]; + + return msg->size; +} + +/* Clears the shared store; call once per test before initialising any AUX. */ +static void dm_mst_test_reset_dsc_dpcd(void) +{ + memset(dm_mst_test_dsc_dpcd, 0, sizeof(dm_mst_test_dsc_dpcd)); + dm_mst_test_dsc_aux_fail = NULL; +} + +static void dm_mst_test_init_dsc_aux(struct drm_dp_aux *aux, const char *name) +{ + aux->name = name; + aux->transfer = dm_mst_test_dsc_aux_transfer; + drm_dp_aux_init(aux); + drm_dp_dpcd_set_probe(aux, false); +} + +static struct drm_dp_aux *dm_mst_test_alloc_dsc_aux(struct kunit *test, const char *name) +{ + struct drm_dp_aux *aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, aux); + dm_mst_test_init_dsc_aux(aux, name); + + return aux; +} + +/* Tests for dp_get_link_current_set_bw */ + +/* + * Program DPCD 0x100..0x10f, the 16-byte window the helper reads in one go. + * @coding is the raw DP_MAIN_LINK_CHANNEL_CODING_SET byte at 0x108. + */ +static void dm_mst_test_set_link_settings(u8 link_bw_set, u8 lane_count, u8 coding) +{ + dm_mst_test_dsc_dpcd[DP_LINK_BW_SET] = link_bw_set; + dm_mst_test_dsc_dpcd[DP_LANE_COUNT_SET] = lane_count; + dm_mst_test_dsc_dpcd[DP_MAIN_LINK_CHANNEL_CODING_SET] = coding; +} + +struct dm_mst_link_bw_param { + const char *name; + u8 link_bw_set; + u8 lane_count; + u8 coding; + bool aux_fails; + bool supported; + uint32_t cur_link_bw; +}; + +static const struct dm_mst_link_bw_param dm_mst_link_bw_params[] = { + { "hbr2_8b_10b", DP_LINK_BW_5_4, 4, DP_8b_10b_ENCODING, false, true, 16761600 }, + { "uhbr10", DP_LINK_BW_10, 4, DP_128b_132b_ENCODING, false, true, 38564000 }, + { "uhbr13_5", DP_LINK_BW_13_5, 4, DP_128b_132b_ENCODING, false, true, 52061400 }, + { "uhbr20", DP_LINK_BW_20, 4, DP_128b_132b_ENCODING, false, true, 77128000 }, + { "unlisted_uhbr_rate", 0x1e, 4, DP_128b_132b_ENCODING, false, false, 0 }, + { "unknown_encoding", DP_LINK_BW_5_4, 4, DP_UNKNOWN_ENCODING, false, false, 0 }, + { "dpcd_read_error", DP_LINK_BW_5_4, 4, DP_8b_10b_ENCODING, true, false, 0 }, +}; + +KUNIT_ARRAY_PARAM_DESC(dm_mst_link_bw, dm_mst_link_bw_params, name); + +/** + * dm_mst_test_link_current_set_bw - the current link settings are priced + * @test: KUnit test context + * + * For 8b/10b the raw DP_LINK_BW_SET byte is the link rate in 27MHz units, so + * HBR2 (0x14) yields 20 * 27000 * 10 kbps per lane scaled by the 80% data + * efficiency and the 97% FEC efficiency. For 128b/132b the byte instead + * selects a UHBR rate scaled by its own efficiency. An unlisted UHBR rate, an + * unknown channel coding and a short DPCD read all report no bandwidth. + */ +static void dm_mst_test_link_current_set_bw(struct kunit *test) +{ + const struct dm_mst_link_bw_param *param = test->param_value; + uint32_t cur_link_bw = 0xdeadbeef; + struct drm_dp_aux *aux; + + dm_mst_test_reset_dsc_dpcd(); + aux = dm_mst_test_alloc_dsc_aux(test, "dm_mst_test_link_bw_aux"); + dm_mst_test_set_link_settings(param->link_bw_set, param->lane_count, param->coding); + if (param->aux_fails) + dm_mst_test_dsc_aux_fail = aux; + + KUNIT_EXPECT_EQ(test, dp_get_link_current_set_bw(aux, &cur_link_bw), param->supported); + KUNIT_EXPECT_EQ(test, cur_link_bw, param->cur_link_bw); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -2360,6 +2478,8 @@ static struct kunit_case dm_mst_types_test_cases[] = { /* dm_dp_mst_connector_destroy tests */ KUNIT_CASE(dm_mst_test_connector_destroy_no_sink), KUNIT_CASE(dm_mst_test_connector_destroy_releases_sink), + /* dp_get_link_current_set_bw tests */ + KUNIT_CASE_PARAM(dm_mst_test_link_current_set_bw, dm_mst_link_bw_gen_params), {} }; From 4f168a2d76948ee742e9f100777b01bc35bdb25d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:04:38 -0600 Subject: [PATCH 026/114] drm/amd/display: Test cascaded Panamera check [WHAT] Add KUnit tests for is_synaptics_cascaded_panamera() covering a matching cascaded hub, a non Synaptics branch, a non Panamera device ID, an unsupported branch firmware revision and a port without a parent branch. [HOW] Allocate an MST topology manager with a branch and a port, and drive a table driven test that fills in the branch OUI, device ID and firmware revision for each case before checking the returned verdict. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 1 + .../tests/amdgpu_dm_mst_types_test.c | 81 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index e8f4ca7e1f2a..c584906c8bb0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -279,7 +279,7 @@ bool needs_dsc_aux_workaround(struct dc_link *link) EXPORT_IF_KUNIT(needs_dsc_aux_workaround); #if defined(CONFIG_DRM_AMD_DC_FP) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) -static bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port) +STATIC_IFN_KUNIT bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port) { u8 branch_vendor_data[4] = { 0 }; // Vendor data 0x50C ~ 0x50F @@ -293,6 +293,7 @@ static bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_m return false; } +EXPORT_IF_KUNIT(is_synaptics_cascaded_panamera); static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 4af3aac31223..f2e36807f0da 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -105,6 +105,7 @@ enum dc_status dm_dp_mst_is_port_support_mode( #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector); +bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port); bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 58a4bbb31e79..2e562c545c30 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -2394,6 +2394,85 @@ static void dm_mst_test_link_current_set_bw(struct kunit *test) KUNIT_EXPECT_EQ(test, cur_link_bw, param->cur_link_bw); } +/* Tests for is_synaptics_cascaded_panamera */ + +/* + * A Panamera hub is identified by its branch device ID plus the high nibble of + * the fifth branch device name byte; the cascaded variant additionally reports + * SYNAPTICS_CASCADED_HUB_ID at DPCD 0x50e. + */ +static void dm_mst_test_set_panamera_ids(struct dc_link *link, u32 branch_dev_id, + u8 dev_name_4, u8 cascaded_id) +{ + link->dpcd_caps.branch_dev_id = branch_dev_id; + link->dpcd_caps.branch_dev_name[4] = dev_name_4; + dm_mst_test_dsc_dpcd[DP_BRANCH_VENDOR_SPECIFIC_START + 2] = cascaded_id; +} + +static struct drm_dp_mst_port *dm_mst_test_alloc_mgr_port(struct kunit *test) +{ + struct drm_dp_mst_topology_mgr *mgr; + struct drm_dp_mst_port *port; + + mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, mgr); + KUNIT_ASSERT_NOT_NULL(test, port); + + mutex_init(&mgr->lock); + mgr->aux = dm_mst_test_alloc_dsc_aux(test, "dm_mst_test_mgr_aux"); + port->mgr = mgr; + + return port; +} + +struct dm_mst_panamera_param { + const char *name; + u32 branch_dev_id; + u8 dev_name_4; + u8 cascaded_id; + bool aux_fails; + bool cascaded; +}; + +static const struct dm_mst_panamera_param dm_mst_panamera_params[] = { + { "cascaded_hub", DP_BRANCH_DEVICE_ID_90CC24, 0x50, + SYNAPTICS_CASCADED_HUB_ID, false, true }, + { "wrong_dev_id", 0x001122, 0x50, SYNAPTICS_CASCADED_HUB_ID, false, false }, + { "not_panamera", DP_BRANCH_DEVICE_ID_90CC24, 0x40, + SYNAPTICS_CASCADED_HUB_ID, false, false }, + { "not_cascaded", DP_BRANCH_DEVICE_ID_90CC24, 0x50, 0, false, false }, + { "dpcd_read_error", DP_BRANCH_DEVICE_ID_90CC24, 0x50, + SYNAPTICS_CASCADED_HUB_ID, true, false }, +}; + +KUNIT_ARRAY_PARAM_DESC(dm_mst_panamera, dm_mst_panamera_params, name); + +/** + * dm_mst_test_synaptics_cascaded - only a cascaded Panamera hub is detected + * @test: KUnit test context + * + * All three conditions must hold: the Synaptics branch device ID, a Panamera + * device name and the cascaded hub ID in the branch vendor data. A failed + * vendor read leaves the hub undetected. + */ +static void dm_mst_test_synaptics_cascaded(struct kunit *test) +{ + const struct dm_mst_panamera_param *param = test->param_value; + struct drm_dp_mst_port *port; + struct dc_link *link; + + dm_mst_test_reset_dsc_dpcd(); + port = dm_mst_test_alloc_mgr_port(test); + link = dm_kunit_alloc_link(test); + dm_mst_test_set_panamera_ids(link, param->branch_dev_id, param->dev_name_4, + param->cascaded_id); + if (param->aux_fails) + dm_mst_test_dsc_aux_fail = port->mgr->aux; + + KUNIT_EXPECT_EQ(test, is_synaptics_cascaded_panamera(link, port), param->cascaded); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -2480,6 +2559,8 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_connector_destroy_releases_sink), /* dp_get_link_current_set_bw tests */ KUNIT_CASE_PARAM(dm_mst_test_link_current_set_bw, dm_mst_link_bw_gen_params), + /* is_synaptics_cascaded_panamera tests */ + KUNIT_CASE_PARAM(dm_mst_test_synaptics_cascaded, dm_mst_panamera_gen_params), {} }; From da3fb3a15e17b1809ab893ffbe8d40fb984d4729 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:04:51 -0600 Subject: [PATCH 027/114] drm/amd/display: Test DSC caps validation [WHAT] Add KUnit tests for validate_dsc_caps_on_connector() covering a port without a DSC AUX channel, the branch AUX workaround, the cascaded Panamera hub override, a DSC caps read failure and a decoder that does not advertise DSC. [HOW] Attach the absolute DPCD fake to both the root and the port AUX channels and populate the DSC capability block, then select each path by tuning the branch descriptor and the topology, and check which AUX channel is recorded along with the parsed DSC capabilities. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 1 + .../tests/amdgpu_dm_mst_types_test.c | 153 ++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index c584906c8bb0..cb202b28a37c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -295,7 +295,7 @@ STATIC_IFN_KUNIT bool is_synaptics_cascaded_panamera(struct dc_link *link, struc } EXPORT_IF_KUNIT(is_synaptics_cascaded_panamera); -static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector) { struct dc_sink *dc_sink = aconnector->dc_sink; struct drm_dp_mst_port *port = aconnector->mst_output_port; @@ -339,6 +339,7 @@ static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnecto return true; } +EXPORT_IF_KUNIT(validate_dsc_caps_on_connector); #endif STATIC_IFN_KUNIT bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index f2e36807f0da..76db9a8330e5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -107,6 +107,7 @@ enum dc_status dm_dp_mst_is_port_support_mode( void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector); bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port); bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw); +bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); ssize_t dm_dp_aux_transfer_result(ssize_t result, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 2e562c545c30..f74d263cfabf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -2473,6 +2473,153 @@ static void dm_mst_test_synaptics_cascaded(struct kunit *test) KUNIT_EXPECT_EQ(test, is_synaptics_cascaded_panamera(link, port), param->cascaded); } +/* Tests for validate_dsc_caps_on_connector */ + +struct dm_mst_test_dsc_ctx { + struct amdgpu_dm_connector *aconnector; + struct amdgpu_dm_connector *root; + struct drm_dp_mst_port *port; + struct dc_sink *sink; + struct dc_link *link; +}; + +/* + * Build an MST endpoint whose port hangs directly off the primary branch. With + * an all-zero DPCD, drm_dp_mst_dsc_aux_for_port() finds no DSC-capable AUX, so + * each test can steer the driver's own fallbacks in isolation. + */ +static void dm_mst_test_init_dsc_ctx(struct kunit *test, struct dm_mst_test_dsc_ctx *ctx) +{ + struct drm_dp_mst_branch *branch; + struct dc *dc; + + dm_mst_test_reset_dsc_dpcd(); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + ctx->root = kunit_kzalloc(test, sizeof(*ctx->root), GFP_KERNEL); + ctx->sink = kunit_kzalloc(test, sizeof(*ctx->sink), GFP_KERNEL); + branch = kunit_kzalloc(test, sizeof(*branch), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + KUNIT_ASSERT_NOT_NULL(test, ctx->root); + KUNIT_ASSERT_NOT_NULL(test, ctx->sink); + KUNIT_ASSERT_NOT_NULL(test, branch); + + dc = dm_kunit_alloc_dc_with_ctx(test); + ctx->link = dm_kunit_alloc_link(test); + ctx->link->ctx = dc->ctx; + + ctx->port = dm_mst_test_alloc_mgr_port(test); + ctx->port->parent = branch; + dm_mst_test_init_dsc_aux(&ctx->port->aux, "dm_mst_test_port_dsc_aux"); + dm_mst_test_init_dsc_aux(&ctx->root->dm_dp_aux.aux, "dm_mst_test_root_dsc_aux"); + + ctx->aconnector->dc_link = ctx->link; + ctx->aconnector->dc_sink = ctx->sink; + ctx->aconnector->mst_output_port = ctx->port; + ctx->aconnector->mst_root = ctx->root; +} + +/* Make needs_dsc_aux_workaround() accept the link. */ +static void dm_mst_test_arm_dsc_aux_workaround(struct dc_link *link) +{ + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 2; +} + +/** + * dm_mst_test_validate_dsc_caps_no_aux - no DSC AUX means no DSC capabilities + * @test: KUnit test context + * + * When neither the DRM helper, the Synaptics workaround nor the cascaded hub + * quirk yields a DSC AUX channel, validation must fail without touching DPCD. + */ +static void dm_mst_test_validate_dsc_caps_no_aux(struct kunit *test) +{ + struct dm_mst_test_dsc_ctx ctx; + + dm_mst_test_init_dsc_ctx(test, &ctx); + + KUNIT_EXPECT_FALSE(test, validate_dsc_caps_on_connector(ctx.aconnector)); + KUNIT_EXPECT_NULL(test, ctx.aconnector->dsc_aux); +} + +/** + * dm_mst_test_validate_dsc_caps_aux_workaround - MST dock AUX fallback is used + * @test: KUnit test context + * + * A DSC capable dock in front of a non-DSC display leaves the DRM helper + * without an AUX, so the driver must fall back to the MST root's AUX and parse + * the DSC capabilities read from it. + */ +static void dm_mst_test_validate_dsc_caps_aux_workaround(struct kunit *test) +{ + struct dm_mst_test_dsc_ctx ctx; + + dm_mst_test_init_dsc_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + dm_mst_test_dsc_dpcd[DP_DSC_SUPPORT] = DP_DSC_DECOMPRESSION_IS_SUPPORTED; + + KUNIT_EXPECT_TRUE(test, validate_dsc_caps_on_connector(ctx.aconnector)); + KUNIT_EXPECT_PTR_EQ(test, ctx.aconnector->dsc_aux, &ctx.root->dm_dp_aux.aux); + KUNIT_EXPECT_TRUE(test, ctx.sink->dsc_caps.dsc_dec_caps.is_dsc_supported); +} + +/** + * dm_mst_test_validate_dsc_caps_cascaded_hub - cascaded hub uses the mgr AUX + * @test: KUnit test context + * + * On a cascaded Synaptics Panamera hub the DSC decoder lives on the primary + * branch, so the topology manager's AUX must win over every other fallback. + */ +static void dm_mst_test_validate_dsc_caps_cascaded_hub(struct kunit *test) +{ + struct dm_mst_test_dsc_ctx ctx; + + dm_mst_test_init_dsc_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + dm_mst_test_set_panamera_ids(ctx.link, DP_BRANCH_DEVICE_ID_90CC24, 0x50, + SYNAPTICS_CASCADED_HUB_ID); + dm_mst_test_dsc_dpcd[DP_DSC_SUPPORT] = DP_DSC_DECOMPRESSION_IS_SUPPORTED; + + KUNIT_EXPECT_TRUE(test, validate_dsc_caps_on_connector(ctx.aconnector)); + KUNIT_EXPECT_PTR_EQ(test, ctx.aconnector->dsc_aux, ctx.port->mgr->aux); +} + +/** + * dm_mst_test_validate_dsc_caps_read_error - a failed DSC caps read is fatal + * @test: KUnit test context + */ +static void dm_mst_test_validate_dsc_caps_read_error(struct kunit *test) +{ + struct dm_mst_test_dsc_ctx ctx; + + dm_mst_test_init_dsc_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + dm_mst_test_dsc_aux_fail = &ctx.root->dm_dp_aux.aux; + + KUNIT_EXPECT_FALSE(test, validate_dsc_caps_on_connector(ctx.aconnector)); + KUNIT_EXPECT_PTR_EQ(test, ctx.aconnector->dsc_aux, &ctx.root->dm_dp_aux.aux); +} + +/** + * dm_mst_test_validate_dsc_caps_unsupported - a sink without DSC is rejected + * @test: KUnit test context + * + * The AUX resolves but the sink reports no decompression support, so parsing + * the raw capabilities fails and the connector must not be marked DSC capable. + */ +static void dm_mst_test_validate_dsc_caps_unsupported(struct kunit *test) +{ + struct dm_mst_test_dsc_ctx ctx; + + dm_mst_test_init_dsc_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + + KUNIT_EXPECT_FALSE(test, validate_dsc_caps_on_connector(ctx.aconnector)); + KUNIT_EXPECT_FALSE(test, ctx.sink->dsc_caps.dsc_dec_caps.is_dsc_supported); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -2561,6 +2708,12 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE_PARAM(dm_mst_test_link_current_set_bw, dm_mst_link_bw_gen_params), /* is_synaptics_cascaded_panamera tests */ KUNIT_CASE_PARAM(dm_mst_test_synaptics_cascaded, dm_mst_panamera_gen_params), + /* validate_dsc_caps_on_connector tests */ + KUNIT_CASE(dm_mst_test_validate_dsc_caps_no_aux), + KUNIT_CASE(dm_mst_test_validate_dsc_caps_aux_workaround), + KUNIT_CASE(dm_mst_test_validate_dsc_caps_cascaded_hub), + KUNIT_CASE(dm_mst_test_validate_dsc_caps_read_error), + KUNIT_CASE(dm_mst_test_validate_dsc_caps_unsupported), {} }; From 814fdfcfdedf654b5d37dc90db1223e0684886f6 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:05:04 -0600 Subject: [PATCH 028/114] drm/amd/display: Test MST port mode support [WHAT] Add KUnit tests for dm_dp_mst_is_port_support_mode() covering a mode that fits uncompressed, a port without a DSC AUX channel, a link with no common DSC config, the DSC passthrough accept and reject paths, the last link bandwidth check with a fresh read, a cached value and the Synaptics quirk, the upstream virtual channel limit and the branch throughput limit. [HOW] Build an MST connector with a stream and a DSC capable encoder, using a fake dsc_funcs that reports encoder capabilities so the DC bandwidth range helpers produce usable values. Each path is then selected by varying the timing, the port DSC capabilities, the full and available PBN of the topology and the branch descriptor. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../tests/amdgpu_dm_mst_types_test.c | 370 ++++++++++++++++++ 1 file changed, 370 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index f74d263cfabf..13c141d9963a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -29,6 +29,8 @@ #include "amdgpu_dm_hdcp.h" #include "amdgpu_dm_mst_types.h" #include "amdgpu_dm_kunit_test_helpers.h" +#include "dsc/dsc.h" +#include "inc/core_types.h" #include "inc/link_service.h" /* @@ -2620,6 +2622,363 @@ static void dm_mst_test_validate_dsc_caps_unsupported(struct kunit *test) KUNIT_EXPECT_FALSE(test, ctx.sink->dsc_caps.dsc_dec_caps.is_dsc_supported); } +/* Tests for dm_dp_mst_is_port_support_mode */ + +static uint32_t dm_mst_test_root_link_bw_kbps; + +static uint32_t dm_mst_test_root_bandwidth_kbps(const struct dc_link *link, + const struct dc_link_settings *link_settings) +{ + return dm_mst_test_root_link_bw_kbps; +} + +struct dm_mst_test_port_mode_ctx { + struct amdgpu_dm_connector *aconnector; + struct dc_stream_state *stream; + struct drm_dp_mst_port *port; + struct dc_sink *sink; + struct dc_link *link; + struct dc *dc; +}; + +/* + * Build the minimum needed to price a mode: a link whose root bandwidth is + * faked, a virtual channel sized by the port's full_pbn, and a 24bpp timing + * that needs pix_clk_100hz / 10 * 24 kbps uncompressed. + */ +static void dm_mst_test_init_port_mode_ctx(struct kunit *test, + struct dm_mst_test_port_mode_ctx *ctx, + uint32_t root_link_bw_kbps, u32 full_pbn, + uint32_t pix_clk_100hz) +{ + struct link_service *link_srv; + struct resource_pool *res_pool; + + dm_mst_test_reset_dsc_dpcd(); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + ctx->sink = kunit_kzalloc(test, sizeof(*ctx->sink), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + KUNIT_ASSERT_NOT_NULL(test, ctx->sink); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, res_pool); + + dm_mst_test_root_link_bw_kbps = root_link_bw_kbps; + link_srv->dp_link_bandwidth_kbps = dm_mst_test_root_bandwidth_kbps; + + ctx->dc = dm_kunit_alloc_dc_with_ctx(test); + ctx->dc->link_srv = link_srv; + ctx->dc->res_pool = res_pool; + + ctx->link = dm_kunit_alloc_link(test); + ctx->link->dc = ctx->dc; + ctx->link->ctx = ctx->dc->ctx; + + ctx->sink->ctx = ctx->dc->ctx; + ctx->port = dm_mst_test_alloc_mgr_port(test); + ctx->port->full_pbn = full_pbn; + + ctx->stream = dm_kunit_alloc_stream(test, ctx->link); + ctx->stream->sink = ctx->sink; + ctx->stream->timing.display_color_depth = COLOR_DEPTH_888; + ctx->stream->timing.pix_clk_100hz = pix_clk_100hz; + + ctx->aconnector->dc_link = ctx->link; + ctx->aconnector->dc_sink = ctx->sink; + ctx->aconnector->mst_output_port = ctx->port; +} + +/** + * dm_mst_test_port_mode_fits_without_dsc - sufficient bandwidth needs no DSC + * @test: KUnit test context + * + * When the uncompressed stream fits into the smaller of the root link and the + * virtual channel bandwidth, the mode is accepted before any DSC evaluation. + */ +static void dm_mst_test_port_mode_fits_without_dsc(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 1000000, 1000, 100000); + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), DC_OK); + KUNIT_EXPECT_EQ(test, (u32)ctx.stream->timing.flags.DSC, 0U); +} + +/** + * dm_mst_test_port_mode_no_dsc_aux - DSC is required but unavailable + * @test: KUnit test context + * + * A mode that exceeds the end-to-end bandwidth on a connector without a DSC + * AUX channel cannot be compressed, so validation must fail. + */ +static void dm_mst_test_port_mode_no_dsc_aux(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 100000, 10, 100000); + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); +} + +/** + * dm_mst_test_port_mode_no_common_dsc_config - no shared DSC config is fatal + * @test: KUnit test context + * + * With a DSC AUX but no DSC encoder in the resource pool, source and sink share + * no usable DSC configuration and the mode must be rejected. + */ +static void dm_mst_test_port_mode_no_common_dsc_config(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 100000, 10, 100000); + ctx.aconnector->dsc_aux = ctx.port->mgr->aux; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); +} + +/* + * 1920x1080@60 RGB 8bpc needs 3564000 kbps uncompressed, and roughly 1188000 + * kbps at the 8bpp DSC policy minimum. + */ +#define DM_MST_TEST_DSC_PIX_CLK_100HZ 1485000 + +/* Enough of dsc2_get_enc_caps() for the DSC policy and bandwidth maths. */ +static void dm_mst_test_dsc_get_enc_caps(struct dsc_enc_caps *dsc_enc_caps, int pixel_clock_100Hz) +{ + dsc_enc_caps->dsc_version = 0x21; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_1 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_2 = 1; + dsc_enc_caps->slice_caps.bits.NUM_SLICES_4 = 1; + dsc_enc_caps->lb_bit_depth = 13; + dsc_enc_caps->is_block_pred_supported = true; + dsc_enc_caps->color_formats.bits.RGB = 1; + dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; + dsc_enc_caps->max_total_throughput_mps = 4800; + dsc_enc_caps->max_slice_width = 5184; + dsc_enc_caps->bpp_increment_div = 16; +} + +static const struct dsc_funcs dm_mst_test_dsc_funcs = { + .dsc_get_enc_caps = dm_mst_test_dsc_get_enc_caps, +}; + +/* Install the DSC encoder fake and matching sink decoder capabilities. */ +static void dm_mst_test_setup_dsc_caps(struct kunit *test, struct dc *dc, struct dc_sink *sink) +{ + struct dsc_dec_dpcd_caps *dec = &sink->dsc_caps.dsc_dec_caps; + struct display_stream_compressor *dsc; + + dsc = kunit_kzalloc(test, sizeof(*dsc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dsc); + + dsc->funcs = &dm_mst_test_dsc_funcs; + dsc->ctx = dc->ctx; + dc->res_pool->dscs[0] = dsc; + + dec->is_dsc_supported = true; + dec->dsc_version = 0x21; + dec->rc_buffer_size = 16 * 1024; + dec->slice_caps1.bits.NUM_SLICES_1 = 1; + dec->slice_caps1.bits.NUM_SLICES_2 = 1; + dec->slice_caps1.bits.NUM_SLICES_4 = 1; + dec->lb_bit_depth = 13; + dec->is_block_pred_supported = true; + dec->color_formats.bits.RGB = 1; + dec->color_depth.bits.COLOR_DEPTH_8_BPC = 1; + dec->throughput_mode_0_mps = 1000; + dec->throughput_mode_1_mps = 1000; + dec->max_slice_width = 5120; + dec->bpp_increment_div = 16; +} + +static void dm_mst_test_set_dsc_timing(struct dc_crtc_timing *timing) +{ + timing->display_color_depth = COLOR_DEPTH_888; + timing->pixel_encoding = PIXEL_ENCODING_RGB; + timing->h_addressable = 1920; + timing->v_addressable = 1080; + timing->pix_clk_100hz = DM_MST_TEST_DSC_PIX_CLK_100HZ; +} + +/* + * Give the source a DSC encoder and the sink matching decoder capabilities so + * is_dsc_common_config_possible() succeeds, and switch to a timing that does + * not fit uncompressed. + */ +static void dm_mst_test_enable_dsc(struct kunit *test, struct dm_mst_test_port_mode_ctx *ctx) +{ + struct drm_dp_mst_branch *branch; + + branch = kunit_kzalloc(test, sizeof(*branch), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, branch); + + dm_mst_test_setup_dsc_caps(test, ctx->dc, ctx->sink); + dm_mst_test_set_dsc_timing(&ctx->stream->timing); + + ctx->port->parent = branch; + dm_mst_test_init_dsc_aux(&ctx->port->aux, "dm_mst_test_port_mode_aux"); + ctx->aconnector->dsc_aux = ctx->port->mgr->aux; +} + +/** + * dm_mst_test_port_mode_dsc_passthrough_fits - DSC passthrough accepts the mode + * @test: KUnit test context + * + * With DSC passthrough the compressed stream travels the whole path, so a + * minimum compression that fits the end-to-end bandwidth enables DSC. + */ +static void dm_mst_test_port_mode_dsc_passthrough_fits(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->passthrough_aux = ctx.port->mgr->aux; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), DC_OK); + KUNIT_EXPECT_EQ(test, (u32)ctx.stream->timing.flags.DSC, 1U); +} + +/** + * dm_mst_test_port_mode_dsc_passthrough_too_narrow - max compression still too big + * @test: KUnit test context + * + * When even the smallest DSC bitstream exceeds the end-to-end bandwidth the + * mode must be rejected instead of enabling DSC. + */ +static void dm_mst_test_port_mode_dsc_passthrough_too_narrow(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 1000000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->passthrough_aux = ctx.port->mgr->aux; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); + KUNIT_EXPECT_EQ(test, (u32)ctx.stream->timing.flags.DSC, 0U); +} + +/** + * dm_mst_test_port_mode_last_link_too_slow - the uncompressed last link is checked + * @test: KUnit test context + * + * DSC is decoded at the endpoint, so the last DP link still carries the + * uncompressed stream. Its current link settings are read from DPCD and cached + * on the connector, and a mode that does not fit is rejected. + */ +static void dm_mst_test_port_mode_last_link_too_slow(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->pdt = DP_PEER_DEVICE_SST_SINK; + /* RBR x1: 1257120 kbps, far below the 3564000 kbps the mode needs. */ + dm_mst_test_set_link_settings(DP_LINK_BW_1_62, 1, DP_8b_10b_ENCODING); + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); + KUNIT_EXPECT_EQ(test, ctx.aconnector->mst_local_bw, 1257120U); + KUNIT_EXPECT_EQ(test, ctx.aconnector->vc_full_pbn, ctx.port->full_pbn); +} + +/** + * dm_mst_test_port_mode_last_link_cached_bw - a cached last link bandwidth is reused + * @test: KUnit test context + * + * While the virtual channel allocation is unchanged the previously read link + * bandwidth is reused instead of going out to DPCD again. + */ +static void dm_mst_test_port_mode_last_link_cached_bw(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->pdt = DP_PEER_DEVICE_SST_SINK; + ctx.aconnector->vc_full_pbn = ctx.port->full_pbn; + ctx.aconnector->mst_local_bw = 100000; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); + KUNIT_EXPECT_EQ(test, ctx.aconnector->mst_local_bw, 100000U); +} + +/** + * dm_mst_test_port_mode_last_link_synaptics_quirk - Synaptics hubs skip the check + * @test: KUnit test context + * + * Synaptics branch devices misreport their last link settings, so the last + * link bandwidth check is skipped for them and the mode is accepted. + */ +static void dm_mst_test_port_mode_last_link_synaptics_quirk(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->pdt = DP_PEER_DEVICE_SST_SINK; + ctx.aconnector->branch_ieee_oui = DP_BRANCH_DEVICE_ID_90CC24; + dm_mst_test_set_link_settings(DP_LINK_BW_1_62, 1, DP_8b_10b_ENCODING); + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), DC_OK); + KUNIT_EXPECT_EQ(test, (u32)ctx.stream->timing.flags.DSC, 1U); +} + +/** + * dm_mst_test_port_mode_upstream_vc_too_small - the upstream hop is the bottleneck + * @test: KUnit test context + * + * For a nested topology the virtual channel of the link before the last one + * caps the compressed bandwidth, and a mode that exceeds it is rejected. + */ +static void dm_mst_test_port_mode_upstream_vc_too_small(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + struct drm_dp_mst_port *upstream; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->pdt = DP_PEER_DEVICE_NONE; + + upstream = kunit_kzalloc(test, sizeof(*upstream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, upstream); + /* 100 PBN is about 670950 kbps, below the DSC minimum for this mode. */ + upstream->full_pbn = 100; + ctx.port->parent->port_parent = upstream; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); +} + +/** + * dm_mst_test_port_mode_branch_throughput_exceeded - branch decoder limit applies + * @test: KUnit test context + * + * Even with a valid DSC configuration the branch decoder's overall throughput + * for RGB must be able to carry the pixel rate. + */ +static void dm_mst_test_port_mode_branch_throughput_exceeded(struct kunit *test) +{ + struct dm_mst_test_port_mode_ctx ctx; + + dm_mst_test_init_port_mode_ctx(test, &ctx, 2500000, 1000, 100000); + dm_mst_test_enable_dsc(test, &ctx); + ctx.port->passthrough_aux = ctx.port->mgr->aux; + /* 100 Mpix/s cannot carry the 148.5 Mpix/s this mode needs. */ + ctx.sink->dsc_caps.dsc_dec_caps.branch_overall_throughput_0_mps = 100; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(ctx.aconnector, ctx.stream), + DC_FAIL_BANDWIDTH_VALIDATE); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -2714,6 +3073,17 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_validate_dsc_caps_cascaded_hub), KUNIT_CASE(dm_mst_test_validate_dsc_caps_read_error), KUNIT_CASE(dm_mst_test_validate_dsc_caps_unsupported), + /* dm_dp_mst_is_port_support_mode tests */ + KUNIT_CASE(dm_mst_test_port_mode_fits_without_dsc), + KUNIT_CASE(dm_mst_test_port_mode_no_dsc_aux), + KUNIT_CASE(dm_mst_test_port_mode_no_common_dsc_config), + KUNIT_CASE(dm_mst_test_port_mode_dsc_passthrough_fits), + KUNIT_CASE(dm_mst_test_port_mode_dsc_passthrough_too_narrow), + KUNIT_CASE(dm_mst_test_port_mode_last_link_too_slow), + KUNIT_CASE(dm_mst_test_port_mode_last_link_cached_bw), + KUNIT_CASE(dm_mst_test_port_mode_last_link_synaptics_quirk), + KUNIT_CASE(dm_mst_test_port_mode_upstream_vc_too_small), + KUNIT_CASE(dm_mst_test_port_mode_branch_throughput_exceeded), {} }; From 85333609b17e04574cad0050b7a64f386e727e8d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:05:18 -0600 Subject: [PATCH 029/114] drm/amd/display: Test FRL bandwidth lookup [WHAT] Add KUnit tests for get_conv_frl_bw() covering a protocol converter without FRL support, a downstream port that is not HDMI, a sink whose maximum FRL rate is zero and the case where the converter rate is the bottleneck. [HOW] Populate the display info and the downstream port capabilities of the connector for each case and check both the returned verdict and the bandwidth values it reports. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 5 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 2 + .../tests/amdgpu_dm_mst_types_test.c | 135 ++++++++++++++++++ 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index cb202b28a37c..4c6e7c28e4f8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1231,8 +1231,8 @@ static int try_disable_dsc(struct drm_atomic_commit *state, return 0; } -static bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, - uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps) +STATIC_IFN_KUNIT bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, + uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps) { unsigned int max_conv_bw_in_kbps = 0; unsigned int max_sink_bw_in_kbps = 0; @@ -1258,6 +1258,7 @@ static bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, return *bw_in_kbps > 0; // Frl endpoint is detected } +EXPORT_IF_KUNIT(get_conv_frl_bw); static void build_frl_mst_dsc_params(struct amdgpu_dm_connector *aconnector, struct dc_stream_state *stream, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 76db9a8330e5..e1a25afb570c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -107,6 +107,8 @@ enum dc_status dm_dp_mst_is_port_support_mode( void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector); bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port); bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw); +bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, + uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps); bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 13c141d9963a..0e5dd4ca9aed 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -2979,6 +2979,136 @@ static void dm_mst_test_port_mode_branch_throughput_exceeded(struct kunit *test) DC_FAIL_BANDWIDTH_VALIDATE); } +/* Tests for get_conv_frl_bw */ + +/* Deterministic stand-in for the DC raw-FRL-rate lookup table. */ +static uint32_t dm_mst_test_bw_kbps_from_raw_frl(uint8_t bw) +{ + return bw * 3000; +} + +/* + * Wire up a DP-to-HDMI2.1 protocol converter: the PCON capability lives on the + * DC caps, the converter's own limit in the downstream port caps, and the sink + * limits in the EDID caps. + */ +static struct amdgpu_dm_connector *dm_mst_test_alloc_frl_connector(struct kunit *test, + bool pcon_support, + u8 dwn_strm_port_type, + u8 max_encoded_link_bw, + u8 max_frl_rate, + u8 frl_dsc_max_frl_rate) +{ + struct amdgpu_dm_connector *aconnector; + struct link_service *link_srv; + struct dc_sink *sink; + struct dc_link *link; + struct dc *dc; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, sink); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + + link_srv->bw_kbps_from_raw_frl_link_rate_data = dm_mst_test_bw_kbps_from_raw_frl; + + dc = dm_kunit_alloc_dc_with_ctx(test); + dc->link_srv = link_srv; + dc->caps.dp_hdmi21_pcon_support = pcon_support; + + link = dm_kunit_alloc_link(test); + link->dc = dc; + + sink->edid_caps.max_frl_rate = max_frl_rate; + sink->edid_caps.frl_dsc_max_frl_rate = frl_dsc_max_frl_rate; + + aconnector->dc_link = link; + aconnector->dc_sink = sink; + aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE = + dwn_strm_port_type; + aconnector->mst_downstream_port_caps.bytes.byte2.bits.MAX_ENCODED_LINK_BW_SUPPORT = + max_encoded_link_bw; + + return aconnector; +} + +/** + * dm_mst_test_conv_frl_bw_no_pcon_support - DC without PCON support finds no FRL + * @test: KUnit test context + */ +static void dm_mst_test_conv_frl_bw_no_pcon_support(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + uint32_t bw_in_kbps = 0; + uint32_t dsc_bw_in_kbps = 0; + + aconnector = dm_mst_test_alloc_frl_connector(test, false, DOWN_STREAM_DETAILED_HDMI, + 3, 5, 2); + + KUNIT_EXPECT_FALSE(test, get_conv_frl_bw(aconnector, &bw_in_kbps, &dsc_bw_in_kbps)); + KUNIT_EXPECT_EQ(test, bw_in_kbps, 0U); + KUNIT_EXPECT_EQ(test, dsc_bw_in_kbps, 0U); +} + +/** + * dm_mst_test_conv_frl_bw_not_hdmi_port - a non-HDMI downstream port has no FRL + * @test: KUnit test context + */ +static void dm_mst_test_conv_frl_bw_not_hdmi_port(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + uint32_t bw_in_kbps = 0; + uint32_t dsc_bw_in_kbps = 0; + + aconnector = dm_mst_test_alloc_frl_connector(test, true, DOWN_STREAM_DETAILED_DP, 3, 5, 2); + + KUNIT_EXPECT_FALSE(test, get_conv_frl_bw(aconnector, &bw_in_kbps, &dsc_bw_in_kbps)); + KUNIT_EXPECT_EQ(test, bw_in_kbps, 0U); +} + +/** + * dm_mst_test_conv_frl_bw_sink_without_frl - a sink not reporting FRL is skipped + * @test: KUnit test context + * + * Without a sink FRL rate in the EDID there is no endpoint to negotiate with, + * so no bandwidth is reported even though the converter advertises one. + */ +static void dm_mst_test_conv_frl_bw_sink_without_frl(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + uint32_t bw_in_kbps = 0; + uint32_t dsc_bw_in_kbps = 0; + + aconnector = dm_mst_test_alloc_frl_connector(test, true, DOWN_STREAM_DETAILED_HDMI, + 3, 0, 2); + + KUNIT_EXPECT_FALSE(test, get_conv_frl_bw(aconnector, &bw_in_kbps, &dsc_bw_in_kbps)); + KUNIT_EXPECT_EQ(test, bw_in_kbps, 0U); +} + +/** + * dm_mst_test_conv_frl_bw_bottleneck - the converter and sink limits are combined + * @test: KUnit test context + * + * The reported bandwidth is the smaller of the converter and sink FRL rates, + * and the DSC bandwidth is further capped by the sink's DSC FRL rate. + */ +static void dm_mst_test_conv_frl_bw_bottleneck(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + uint32_t bw_in_kbps = 0; + uint32_t dsc_bw_in_kbps = 0; + + aconnector = dm_mst_test_alloc_frl_connector(test, true, DOWN_STREAM_DETAILED_HDMI, + 3, 5, 2); + + KUNIT_EXPECT_TRUE(test, get_conv_frl_bw(aconnector, &bw_in_kbps, &dsc_bw_in_kbps)); + KUNIT_EXPECT_EQ(test, bw_in_kbps, 9000U); + KUNIT_EXPECT_EQ(test, dsc_bw_in_kbps, 6000U); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3084,6 +3214,11 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_port_mode_last_link_synaptics_quirk), KUNIT_CASE(dm_mst_test_port_mode_upstream_vc_too_small), KUNIT_CASE(dm_mst_test_port_mode_branch_throughput_exceeded), + /* get_conv_frl_bw tests */ + KUNIT_CASE(dm_mst_test_conv_frl_bw_no_pcon_support), + KUNIT_CASE(dm_mst_test_conv_frl_bw_not_hdmi_port), + KUNIT_CASE(dm_mst_test_conv_frl_bw_sink_without_frl), + KUNIT_CASE(dm_mst_test_conv_frl_bw_bottleneck), {} }; From 2ddbcae63e506376c79b0adeba404bc6ea002d25 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:05:31 -0600 Subject: [PATCH 030/114] drm/amd/display: Test DSC precompute helpers [WHAT] Add KUnit tests for log_dsc_params(), for find_crtc_index_in_state_by_stream() with a matching and a missing stream, and for is_dsc_precompute_needed() covering an empty state, a stream without a link, a non MST link, an MST link whose CRTC is not in the state and an MST link that needs the precompute pass. [HOW] Hand build a drm_atomic_commit with CRTC states and DC stream states so the stream to CRTC lookup can be driven directly, and reuse it for a table driven is_dsc_precompute_needed() test that varies the link signal type and whether the CRTC is present. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 11 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 4 + .../tests/amdgpu_dm_mst_types_test.c | 174 ++++++++++++++++++ 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 4c6e7c28e4f8..f5085a0036af 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1303,7 +1303,7 @@ static void build_frl_mst_dsc_params(struct amdgpu_dm_connector *aconnector, } } -static void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k) +STATIC_IFN_KUNIT void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k) { int i; @@ -1311,6 +1311,7 @@ static void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k) DRM_DEBUG_DRIVER("MST_DSC DSC params: stream #%d --- dsc_enabled = %d, bpp_x16 = %d, pbn = %d\n", i, vars[i + k].dsc_enabled, vars[i + k].bpp_x16, vars[i + k].pbn); } +EXPORT_IF_KUNIT(log_dsc_params); static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state, struct dc_state *dc_state, @@ -1739,8 +1740,8 @@ static int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state return ret; } -static int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, - struct dc_stream_state *stream) +STATIC_IFN_KUNIT int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, + struct dc_stream_state *stream) { int i; struct drm_crtc *crtc; @@ -1754,6 +1755,7 @@ static int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, } return -1; } +EXPORT_IF_KUNIT(find_crtc_index_in_state_by_stream); static bool is_link_to_dschub(struct dc_link *dc_link) { @@ -1770,7 +1772,7 @@ static bool is_link_to_dschub(struct dc_link *dc_link) return true; } -static bool is_dsc_precompute_needed(struct drm_atomic_commit *state) +STATIC_IFN_KUNIT bool is_dsc_precompute_needed(struct drm_atomic_commit *state) { int i; struct drm_crtc *crtc; @@ -1790,6 +1792,7 @@ static bool is_dsc_precompute_needed(struct drm_atomic_commit *state) } return ret; } +EXPORT_IF_KUNIT(is_dsc_precompute_needed); int pre_validate_dsc(struct drm_atomic_commit *state, struct dm_atomic_state **dm_state_ptr, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index e1a25afb570c..b1d7a21a37e3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -109,6 +109,10 @@ bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw); bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps); +void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k); +int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, + struct dc_stream_state *stream); +bool is_dsc_precompute_needed(struct drm_atomic_commit *state); bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 0e5dd4ca9aed..d91863d5416d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -3109,6 +3109,173 @@ static void dm_mst_test_conv_frl_bw_bottleneck(struct kunit *test) KUNIT_EXPECT_EQ(test, dsc_bw_in_kbps, 6000U); } +/* Tests for log_dsc_params */ + +/** + * dm_mst_test_log_dsc_params - logging the fairness vars leaves them untouched + * @test: KUnit test context + * + * log_dsc_params() only traces, so the only observable contract is that it + * walks @count entries starting at @k without modifying them. + */ +static void dm_mst_test_log_dsc_params(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[3] = { + { .pbn = 100, .dsc_enabled = false, .bpp_x16 = 160 }, + { .pbn = 200, .dsc_enabled = true, .bpp_x16 = 192 }, + { .pbn = 300, .dsc_enabled = true, .bpp_x16 = 256 }, + }; + + log_dsc_params(2, vars, 1); + + KUNIT_EXPECT_EQ(test, vars[1].pbn, 200); + KUNIT_EXPECT_EQ(test, vars[2].bpp_x16, 256); +} + +/* Tests for find_crtc_index_in_state_by_stream and is_dsc_precompute_needed */ + +struct dm_mst_test_crtc_state_ctx { + struct drm_atomic_commit *state; + struct drm_crtc *crtcs; + struct dm_crtc_state *crtc_states; + struct drm_connector *connector; + struct drm_connector_state *conn_state; +}; + +/* + * Hand-build an atomic state with @num_crtc CRTCs, each carrying a DM CRTC + * state. drm_atomic_state_alloc() would need a fully registered mode config, + * and the helpers under test only walk the crtcs/connectors arrays. + */ +static void dm_mst_test_init_crtc_state_ctx(struct kunit *test, + struct dm_mst_test_crtc_state_ctx *ctx, + unsigned int num_crtc) +{ + struct amdgpu_device *adev; + unsigned int i; + + adev = dm_kunit_alloc_adev(test); + adev->ddev.mode_config.num_crtc = num_crtc; + + ctx->state = kunit_kzalloc(test, sizeof(*ctx->state), GFP_KERNEL); + ctx->crtcs = kunit_kcalloc(test, num_crtc, sizeof(*ctx->crtcs), GFP_KERNEL); + ctx->crtc_states = kunit_kcalloc(test, num_crtc, sizeof(*ctx->crtc_states), GFP_KERNEL); + ctx->connector = kunit_kzalloc(test, sizeof(*ctx->connector), GFP_KERNEL); + ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->state); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtcs); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_states); + KUNIT_ASSERT_NOT_NULL(test, ctx->connector); + KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); + + ctx->state->dev = &adev->ddev; + ctx->state->crtcs = kunit_kcalloc(test, num_crtc, sizeof(*ctx->state->crtcs), GFP_KERNEL); + ctx->state->connectors = kunit_kzalloc(test, sizeof(*ctx->state->connectors), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->state->crtcs); + KUNIT_ASSERT_NOT_NULL(test, ctx->state->connectors); + + for (i = 0; i < num_crtc; i++) { + ctx->crtcs[i].dev = &adev->ddev; + ctx->crtc_states[i].base.crtc = &ctx->crtcs[i]; + ctx->state->crtcs[i].ptr = &ctx->crtcs[i]; + ctx->state->crtcs[i].new_state = &ctx->crtc_states[i].base; + ctx->state->crtcs[i].old_state = &ctx->crtc_states[i].base; + } +} + +/* Attach the context's connector to @crtc so the CRTC looks driven. */ +static void dm_mst_test_attach_connector(struct dm_mst_test_crtc_state_ctx *ctx, + struct drm_crtc *crtc) +{ + ctx->conn_state->crtc = crtc; + ctx->state->connectors[0].ptr = ctx->connector; + ctx->state->connectors[0].new_state = ctx->conn_state; + ctx->state->num_connector = 1; +} + +/** + * dm_mst_test_find_crtc_index_matches - the CRTC driving a stream is found + * @test: KUnit test context + */ +static void dm_mst_test_find_crtc_index_matches(struct kunit *test) +{ + struct dm_mst_test_crtc_state_ctx ctx; + struct dc_stream_state *stream; + + dm_mst_test_init_crtc_state_ctx(test, &ctx, 3); + stream = dm_kunit_alloc_stream(test, NULL); + ctx.crtc_states[2].stream = stream; + + KUNIT_EXPECT_EQ(test, find_crtc_index_in_state_by_stream(ctx.state, stream), 2); +} + +/** + * dm_mst_test_find_crtc_index_no_match - an unknown stream yields -1 + * @test: KUnit test context + */ +static void dm_mst_test_find_crtc_index_no_match(struct kunit *test) +{ + struct dm_mst_test_crtc_state_ctx ctx; + struct dc_stream_state *stream; + + dm_mst_test_init_crtc_state_ctx(test, &ctx, 2); + stream = dm_kunit_alloc_stream(test, NULL); + + KUNIT_EXPECT_EQ(test, find_crtc_index_in_state_by_stream(ctx.state, stream), -1); +} + +struct dm_mst_precompute_param { + const char *name; + bool connector_attached; + bool has_stream; + enum dc_connection_type link_type; + bool dsc_support; + bool dsc_passthrough; + bool needed; +}; + +static const struct dm_mst_precompute_param dm_mst_precompute_params[] = { + { "no_connector", false, true, dc_connection_mst_branch, true, false, false }, + { "dsc_hub", true, true, dc_connection_mst_branch, true, false, true }, + { "dsc_passthrough_hub", true, true, dc_connection_mst_branch, false, true, true }, + { "sst_link", true, true, dc_connection_single, true, false, false }, + { "mst_without_dsc", true, true, dc_connection_mst_branch, false, false, false }, + { "no_stream", true, false, dc_connection_mst_branch, true, false, false }, +}; + +KUNIT_ARRAY_PARAM_DESC(dm_mst_precompute, dm_mst_precompute_params, name); + +/** + * dm_mst_test_dsc_precompute_needed - precompute needs a driven DSC MST hub + * @test: KUnit test context + * + * A DSC or DSC passthrough capable MST branch that a connector in the state + * drives needs its bandwidth precomputed. A CRTC no connector drives aborts + * the scan, and SST links or MST branches without DSC never need it. + */ +static void dm_mst_test_dsc_precompute_needed(struct kunit *test) +{ + const struct dm_mst_precompute_param *param = test->param_value; + struct dm_mst_test_crtc_state_ctx ctx; + struct dc_link *link; + + dm_mst_test_init_crtc_state_ctx(test, &ctx, 1); + if (param->connector_attached) + dm_mst_test_attach_connector(&ctx, &ctx.crtcs[0]); + + if (param->has_stream) { + link = dm_kunit_alloc_link(test); + link->type = param->link_type; + link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT = + param->dsc_support; + link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT = + param->dsc_passthrough; + ctx.crtc_states[0].stream = dm_kunit_alloc_stream(test, link); + } + + KUNIT_EXPECT_EQ(test, is_dsc_precompute_needed(ctx.state), param->needed); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3219,6 +3386,13 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_conv_frl_bw_not_hdmi_port), KUNIT_CASE(dm_mst_test_conv_frl_bw_sink_without_frl), KUNIT_CASE(dm_mst_test_conv_frl_bw_bottleneck), + /* log_dsc_params tests */ + KUNIT_CASE(dm_mst_test_log_dsc_params), + /* find_crtc_index_in_state_by_stream tests */ + KUNIT_CASE(dm_mst_test_find_crtc_index_matches), + KUNIT_CASE(dm_mst_test_find_crtc_index_no_match), + /* is_dsc_precompute_needed tests */ + KUNIT_CASE_PARAM(dm_mst_test_dsc_precompute_needed, dm_mst_precompute_gen_params), {} }; From 91b0c8fc506245349e6a28f85a767f7714acfe30 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:05:44 -0600 Subject: [PATCH 031/114] drm/amd/display: Test DSC recompute check [WHAT] Add KUnit tests for is_dsc_need_re_compute() covering a non MST branch, a legacy hub without DSC support, a state with no stream on the link, a mode change, an unchanged stream, a removed stream, a stream without a connector and a connector without a CRTC. [HOW] Build a DC state carrying streams on the link under test together with a matching drm_atomic_commit, then vary the topology, the connector to CRTC mapping and the mode changed flag to select each path. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 2 + .../tests/amdgpu_dm_mst_types_test.c | 228 ++++++++++++++++++ 3 files changed, 232 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index f5085a0036af..fc71f8e567c9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1485,7 +1485,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state, return 0; } -static bool is_dsc_need_re_compute( +STATIC_IFN_KUNIT bool is_dsc_need_re_compute( struct drm_atomic_commit *state, struct dc_state *dc_state, struct dc_link *dc_link) @@ -1616,6 +1616,7 @@ out: return is_dsc_need_re_compute; } +EXPORT_IF_KUNIT(is_dsc_need_re_compute); int compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, struct dc_state *dc_state, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index b1d7a21a37e3..90efb1cf90e1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -113,6 +113,8 @@ void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k); int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, struct dc_stream_state *stream); bool is_dsc_precompute_needed(struct drm_atomic_commit *state); +bool is_dsc_need_re_compute(struct drm_atomic_commit *state, struct dc_state *dc_state, + struct dc_link *dc_link); bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index d91863d5416d..9af4fd84e69c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -3276,6 +3276,225 @@ static void dm_mst_test_dsc_precompute_needed(struct kunit *test) KUNIT_EXPECT_EQ(test, is_dsc_precompute_needed(ctx.state), param->needed); } +/* Tests for is_dsc_need_re_compute */ + +struct dm_mst_test_recompute_ctx { + struct amdgpu_device *adev; + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct dc_state *dc_state; + struct dc_link *link; + struct dc *dc; +}; + +/* + * One MST connector on one CRTC, with an empty new dc_state and an empty + * current dc_state. Tests populate the streams they need and adjust the CRTC + * state flags to select the branch under test. + */ +static void dm_mst_test_init_recompute_ctx(struct kunit *test, + struct dm_mst_test_recompute_ctx *ctx) +{ + struct drm_crtc *crtc; + + ctx->adev = dm_kunit_alloc_adev(test); + ctx->adev->ddev.mode_config.num_crtc = 1; + + ctx->dc = dm_kunit_alloc_dc_with_ctx(test); + ctx->dc->current_state = dm_kunit_alloc_dc_state(test); + ctx->dc_state = dm_kunit_alloc_dc_state(test); + + ctx->link = dm_kunit_alloc_link(test); + ctx->link->dc = ctx->dc; + ctx->link->type = dc_connection_mst_branch; + + ctx->aconnector = dm_kunit_alloc_connector(test, ctx->adev, ctx->link); + ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL); + ctx->state = kunit_kzalloc(test, sizeof(*ctx->state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state); + KUNIT_ASSERT_NOT_NULL(test, ctx->state); + + ctx->state->dev = &ctx->adev->ddev; + ctx->state->crtcs = kunit_kzalloc(test, sizeof(*ctx->state->crtcs), GFP_KERNEL); + ctx->state->connectors = kunit_kzalloc(test, sizeof(*ctx->state->connectors), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->state->crtcs); + KUNIT_ASSERT_NOT_NULL(test, ctx->state->connectors); + + crtc->dev = &ctx->adev->ddev; + ctx->crtc_state->crtc = crtc; + ctx->state->crtcs[0].ptr = crtc; + ctx->state->crtcs[0].new_state = ctx->crtc_state; + ctx->state->crtcs[0].old_state = ctx->crtc_state; + + ctx->conn_state->connector = &ctx->aconnector->base; + ctx->conn_state->crtc = crtc; + ctx->state->connectors[0].ptr = &ctx->aconnector->base; + ctx->state->connectors[0].new_state = ctx->conn_state; + ctx->state->num_connector = 1; +} + +static struct dc_stream_state *dm_mst_test_add_link_stream(struct kunit *test, + struct dc_state *dc_state, + struct dc_link *link, + struct amdgpu_dm_connector *aconnector) +{ + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, link); + + KUNIT_ASSERT_LT(test, dc_state->stream_count, MAX_PIPES); + stream->dm_stream_context = aconnector; + dc_state->streams[dc_state->stream_count++] = stream; + + return stream; +} + +/** + * dm_mst_test_recompute_not_mst_branch - only MST branches are recomputed + * @test: KUnit test context + */ +static void dm_mst_test_recompute_not_mst_branch(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + ctx.link->type = dc_connection_single; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_legacy_hub_without_dsc - old hubs without DSC are skipped + * @test: KUnit test context + * + * A hub matching the no-virtual-DPCD workaround but reporting neither DSC nor + * DSC passthrough support cannot use MST DSC at all. + */ +static void dm_mst_test_recompute_legacy_hub_without_dsc(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_no_stream_on_link - no stream on the link, nothing to do + * @test: KUnit test context + * + * The new state only drives another link, so this hub keeps its current DSC + * configuration. + */ +static void dm_mst_test_recompute_no_stream_on_link(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dc_link *other_link; + + dm_mst_test_init_recompute_ctx(test, &ctx); + other_link = dm_kunit_alloc_link(test); + dm_mst_test_add_link_stream(test, ctx.dc_state, other_link, ctx.aconnector); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_on_mode_change - a modeset on the link forces a recompute + * @test: KUnit test context + */ +static void dm_mst_test_recompute_on_mode_change(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + ctx.crtc_state->mode_changed = true; + + KUNIT_EXPECT_TRUE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_unchanged_stream - an untouched stream needs no recompute + * @test: KUnit test context + * + * The same stream is present in both the new and the current state and its + * CRTC reports no change, so the existing DSC configuration still applies. + */ +static void dm_mst_test_recompute_unchanged_stream(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, ctx.aconnector); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_stream_removed - dropping a stream forces a recompute + * @test: KUnit test context + * + * A stream that is on the link in the current state but absent from the new + * state frees up bandwidth, so the remaining streams must be recomputed. + */ +static void dm_mst_test_recompute_stream_removed(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct amdgpu_dm_connector *gone; + + dm_mst_test_init_recompute_ctx(test, &ctx); + gone = dm_kunit_alloc_connector(test, ctx.adev, ctx.link); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, gone); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + + KUNIT_EXPECT_TRUE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_stream_without_connector - streams need a DM connector + * @test: KUnit test context + * + * A stream on the link whose DM context is not set cannot be attributed to a + * connector, so it does not count towards the streams on the link. + */ +static void dm_mst_test_recompute_stream_without_connector(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, NULL); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_connector_without_crtc - a disabled connector is skipped + * @test: KUnit test context + */ +static void dm_mst_test_recompute_connector_without_crtc(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, ctx.aconnector); + ctx.conn_state->crtc = NULL; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3393,6 +3612,15 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_find_crtc_index_no_match), /* is_dsc_precompute_needed tests */ KUNIT_CASE_PARAM(dm_mst_test_dsc_precompute_needed, dm_mst_precompute_gen_params), + /* is_dsc_need_re_compute tests */ + KUNIT_CASE(dm_mst_test_recompute_not_mst_branch), + KUNIT_CASE(dm_mst_test_recompute_legacy_hub_without_dsc), + KUNIT_CASE(dm_mst_test_recompute_no_stream_on_link), + KUNIT_CASE(dm_mst_test_recompute_on_mode_change), + KUNIT_CASE(dm_mst_test_recompute_unchanged_stream), + KUNIT_CASE(dm_mst_test_recompute_stream_removed), + KUNIT_CASE(dm_mst_test_recompute_stream_without_connector), + KUNIT_CASE(dm_mst_test_recompute_connector_without_crtc), {} }; From b1a0241f5f9a09d1664fa61e76ea9cf38a5687fa Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:05:57 -0600 Subject: [PATCH 032/114] drm/amd/display: Test DSC config computation [WHAT] Add KUnit tests for compute_mst_dsc_configs_for_state() covering an SST stream, a stream with an incomplete topology, a failing stream removal and a link that does not need a recompute, plus a test for pre_validate_dsc() when no precompute pass is required. [HOW] Add a helper that appends an MST stream with a DSC capable link to a DC state, including the resource pool functions the compute pass calls unconditionally, and let each test supply its own stream removal callback to reach the error path. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 2 + .../tests/amdgpu_dm_mst_types_test.c | 150 ++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index fc71f8e567c9..8dc4f81f1716 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1687,6 +1687,7 @@ int compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, return ret; } +EXPORT_IF_KUNIT(compute_mst_dsc_configs_for_state); static int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, struct dc_state *dc_state, @@ -1905,6 +1906,7 @@ clean_exit: return ret; } +EXPORT_IF_KUNIT(pre_validate_dsc); static uint32_t kbps_from_pbn(unsigned int pbn) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 9af4fd84e69c..9bd33317ec1e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -3495,6 +3495,149 @@ static void dm_mst_test_recompute_connector_without_crtc(struct kunit *test) KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); } +/* Tests for compute_mst_dsc_configs_for_state and pre_validate_dsc */ + +typedef enum dc_status (*dm_mst_test_remove_stream_fn)(struct dc *dc, struct dc_state *new_ctx, + struct dc_stream_state *stream); + +static enum dc_status dm_mst_test_remove_stream_fails(struct dc *dc, struct dc_state *new_ctx, + struct dc_stream_state *stream) +{ + return DC_ERROR_UNEXPECTED; +} + +/* + * Add a stream to @ctx that walks as far into the DSC config helpers as the + * caller allows: an MST signal, a DM connector, an MST output port and a DSC + * capable sink. The helpers dereference res_pool->funcs unconditionally, so + * the pool is always given one, carrying @remove_stream. + */ +static struct dc_stream_state * +dm_mst_test_add_mst_dsc_stream(struct kunit *test, struct dm_mst_test_recompute_ctx *ctx, + dm_mst_test_remove_stream_fn remove_stream) +{ + struct dc_stream_state *stream; + struct resource_pool *res_pool; + struct drm_dp_mst_port *port; + struct resource_funcs *funcs; + struct dc_sink *sink; + + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, sink); + KUNIT_ASSERT_NOT_NULL(test, port); + KUNIT_ASSERT_NOT_NULL(test, res_pool); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + funcs->remove_stream_from_ctx = remove_stream; + res_pool->funcs = funcs; + ctx->dc->res_pool = res_pool; + sink->dsc_caps.dsc_dec_caps.is_dsc_supported = true; + ctx->aconnector->dc_sink = sink; + ctx->aconnector->mst_output_port = port; + + stream = dm_mst_test_add_link_stream(test, ctx->dc_state, ctx->link, ctx->aconnector); + stream->ctx = ctx->dc->ctx; + stream->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + + return stream; +} + +/** + * dm_mst_test_compute_configs_skips_sst - non-MST streams are not considered + * @test: KUnit test context + */ +static void dm_mst_test_compute_configs_skips_sst(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dc_stream_state *stream; + + dm_mst_test_init_recompute_ctx(test, &ctx); + stream = dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + stream->ctx = ctx.dc->ctx; + stream->signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_EQ(test, compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/** + * dm_mst_test_compute_configs_skips_incomplete - streams without a sink are skipped + * @test: KUnit test context + * + * An MST stream whose connector has neither a sink nor an output port yet is + * not ready for DSC bandwidth sharing. + */ +static void dm_mst_test_compute_configs_skips_incomplete(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dc_stream_state *stream; + + dm_mst_test_init_recompute_ctx(test, &ctx); + stream = dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + stream->ctx = ctx.dc->ctx; + stream->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + + KUNIT_EXPECT_EQ(test, compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/** + * dm_mst_test_compute_configs_remove_stream_fails - a DC resource failure aborts + * @test: KUnit test context + */ +static void dm_mst_test_compute_configs_remove_stream_fails(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_mst_dsc_stream(test, &ctx, dm_mst_test_remove_stream_fails); + + KUNIT_EXPECT_EQ(test, compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), + -EINVAL); +} + +/** + * dm_mst_test_compute_configs_no_recompute - an unchanged topology is left alone + * @test: KUnit test context + * + * The stream is DSC capable but is_dsc_need_re_compute() reports no change, so + * the existing configuration is kept and no DSC resource is requested. + */ +static void dm_mst_test_compute_configs_no_recompute(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + + dm_mst_test_init_recompute_ctx(test, &ctx); + ctx.link->type = dc_connection_single; + dm_mst_test_add_mst_dsc_stream(test, &ctx, NULL); + + KUNIT_EXPECT_EQ(test, compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/** + * dm_mst_test_pre_validate_dsc_not_needed - precompute is skipped when unneeded + * @test: KUnit test context + * + * Without a DSC capable MST hub in the state there is nothing to precompute, + * so pre_validate_dsc() must succeed without touching the DM atomic state. + */ +static void dm_mst_test_pre_validate_dsc_not_needed(struct kunit *test) +{ + struct dm_mst_test_crtc_state_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_atomic_state *dm_state = NULL; + + dm_mst_test_init_crtc_state_ctx(test, &ctx, 1); + + KUNIT_EXPECT_EQ(test, pre_validate_dsc(ctx.state, &dm_state, vars), 0); + KUNIT_EXPECT_NULL(test, dm_state); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3621,6 +3764,13 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_recompute_stream_removed), KUNIT_CASE(dm_mst_test_recompute_stream_without_connector), KUNIT_CASE(dm_mst_test_recompute_connector_without_crtc), + /* compute_mst_dsc_configs_for_state tests */ + KUNIT_CASE(dm_mst_test_compute_configs_skips_sst), + KUNIT_CASE(dm_mst_test_compute_configs_skips_incomplete), + KUNIT_CASE(dm_mst_test_compute_configs_remove_stream_fails), + KUNIT_CASE(dm_mst_test_compute_configs_no_recompute), + /* pre_validate_dsc tests */ + KUNIT_CASE(dm_mst_test_pre_validate_dsc_not_needed), {} }; From 0371432a8f0e411043ec0c4c9c3454a9b0ff7b62 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sun, 30 Aug 2026 01:06:10 -0600 Subject: [PATCH 033/114] drm/amd/display: Test per-link DSC configs [WHAT] Add KUnit tests for pre_compute_mst_dsc_configs_for_state() covering an SST stream, a stream with an incomplete topology and a link that does not need a recompute, and for compute_mst_dsc_configs_for_link() covering a mode that fits uncompressed, a link that needs compression, a link that runs out of time slots, forced DSC, forced DSC on an already compressed stream, a bits per pixel overwrite, disabled compression and a target that lands between the DSC limits. [HOW] Build an MST topology with a payload allocation and a DC state holding the streams under test, and bind the connector to a CRTC through the atomic helpers so the state teardown keeps the reference counts balanced. Each path is then selected by varying the available time slots and the per stream DSC policy. Assisted-by: Copilot:Claude-Opus-5 Reviewed-by: Bhawanpreet Lakha Signed-off-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 7 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 3 + .../tests/amdgpu_dm_mst_types_test.c | 402 ++++++++++++++++++ 3 files changed, 409 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 8dc4f81f1716..b6b7fd7f8bca 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1689,9 +1689,9 @@ int compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, } EXPORT_IF_KUNIT(compute_mst_dsc_configs_for_state); -static int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, - struct dc_state *dc_state, - struct dsc_mst_fairness_vars *vars) +STATIC_IFN_KUNIT int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, + struct dc_state *dc_state, + struct dsc_mst_fairness_vars *vars) { int i, j; struct dc_stream_state *stream; @@ -1741,6 +1741,7 @@ static int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state return ret; } +EXPORT_IF_KUNIT(pre_compute_mst_dsc_configs_for_state); STATIC_IFN_KUNIT int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, struct dc_stream_state *stream) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 90efb1cf90e1..4436d256a2ae 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -115,6 +115,9 @@ int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state, bool is_dsc_precompute_needed(struct drm_atomic_commit *state); bool is_dsc_need_re_compute(struct drm_atomic_commit *state, struct dc_state *dc_state, struct dc_link *dc_link); +int pre_compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, + struct dc_state *dc_state, + struct dsc_mst_fairness_vars *vars); bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector); bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c index 9bd33317ec1e..382eafab2d43 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -8,9 +8,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -3638,6 +3640,393 @@ static void dm_mst_test_pre_validate_dsc_not_needed(struct kunit *test) KUNIT_EXPECT_NULL(test, dm_state); } +/* Tests for pre_compute_mst_dsc_configs_for_state */ + +/** + * dm_mst_test_pre_compute_configs_skips_sst - non-MST streams are not considered + * @test: KUnit test context + */ +static void dm_mst_test_pre_compute_configs_skips_sst(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dc_stream_state *stream; + + dm_mst_test_init_recompute_ctx(test, &ctx); + stream = dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + stream->ctx = ctx.dc->ctx; + stream->signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/** + * dm_mst_test_pre_compute_configs_skips_incomplete - streams without a sink are skipped + * @test: KUnit test context + */ +static void dm_mst_test_pre_compute_configs_skips_incomplete(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dc_stream_state *stream; + + dm_mst_test_init_recompute_ctx(test, &ctx); + stream = dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + stream->ctx = ctx.dc->ctx; + stream->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/** + * dm_mst_test_pre_compute_configs_no_recompute - an unchanged topology is left alone + * @test: KUnit test context + * + * The stream is DSC capable but is_dsc_need_re_compute() reports no change, so + * the precompute leaves the existing configuration in place. + */ +static void dm_mst_test_pre_compute_configs_no_recompute(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + + dm_mst_test_init_recompute_ctx(test, &ctx); + ctx.link->type = dc_connection_single; + dm_mst_test_add_mst_dsc_stream(test, &ctx, NULL); + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx.state, ctx.dc_state, vars), 0); +} + +/* Tests for compute_mst_dsc_configs_for_link */ + +static const struct drm_connector_funcs dm_mst_test_dsc_link_conn_funcs = { + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static enum dp_link_encoding dm_mst_test_mst_encoding_format(const struct dc_link *link) +{ + return DP_8b_10b_ENCODING; +} + +struct dm_mst_test_dsc_link_ctx { + struct drm_modeset_acquire_ctx acquire_ctx; + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_topology_state *mst_state; + struct drm_atomic_commit *state; + struct dc_stream_state *stream; + struct drm_device *drm; + struct dc_state *dc_state; +}; + +static void dm_mst_test_dsc_link_drop_locks(void *data) +{ + struct dm_mst_test_dsc_link_ctx *ctx = data; + + ctx->drm->mode_config.acquire_ctx = NULL; + drm_modeset_drop_locks(&ctx->acquire_ctx); + drm_modeset_acquire_fini(&ctx->acquire_ctx); +} + +static void dm_mst_test_destroy_mst_mgr(void *data) +{ + struct drm_dp_mst_topology_mgr *mgr = data; + + mgr->mst_state = false; + drm_dp_mst_topology_mgr_destroy(mgr); +} + +/* + * Full fixture for the DSC bandwidth sharing loop: a real DRM pipe and atomic + * state (the DRM MST helpers need private object locking), a real topology + * manager, and one MST stream whose 1920x1080 timing does not fit + * uncompressed. @total_avail_slots decides which compression pass succeeds. + * + * The context is KUnit allocated because the modeset locks are dropped from a + * deferred action, long after the test body has returned. + */ +static struct dm_mst_test_dsc_link_ctx * +dm_mst_test_alloc_dsc_link_ctx(struct kunit *test, int total_avail_slots) +{ + struct dm_mst_test_dsc_link_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct link_service *link_srv; + struct resource_pool *res_pool; + struct drm_dp_mst_port *port; + struct drm_plane *primary; + struct drm_crtc *crtc; + struct dc_sink *sink; + struct dc_link *link; + struct device *dev; + struct dc *dc; + + 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(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + + primary = drm_kunit_helper_create_primary_plane(test, ctx->drm, NULL, NULL, NULL, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + crtc = drm_kunit_helper_create_crtc(test, ctx->drm, primary, NULL, NULL, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + + ctx->aconnector = drmm_kzalloc(ctx->drm, 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_mst_test_dsc_link_conn_funcs, + DRM_MODE_CONNECTOR_DisplayPort, NULL), 0); + drm_mode_config_reset(ctx->drm); + + KUNIT_ASSERT_EQ(test, + drm_dp_mst_topology_mgr_init(&ctx->aconnector->mst_mgr, ctx->drm, + &ctx->aconnector->dm_dp_aux.aux, 16, 4, + ctx->aconnector->base.base.id), 0); + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, dm_mst_test_destroy_mst_mgr, + &ctx->aconnector->mst_mgr), 0); + + drm_modeset_acquire_init(&ctx->acquire_ctx, 0); + ctx->drm->mode_config.acquire_ctx = &ctx->acquire_ctx; + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, dm_mst_test_dsc_link_drop_locks, ctx), 0); + + ctx->state = drm_kunit_helper_atomic_state_alloc(test, ctx->drm, &ctx->acquire_ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->state); + + crtc_state = drm_atomic_get_crtc_state(ctx->state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + crtc_state->enable = true; + crtc_state->active = true; + crtc_state->mode_changed = true; + + conn_state = drm_atomic_get_connector_state(ctx->state, &ctx->aconnector->base); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + /* Takes the connector reference that the state teardown drops again. */ + KUNIT_ASSERT_EQ(test, drm_atomic_set_crtc_for_connector(conn_state, crtc), 0); + + ctx->mst_state = drm_atomic_get_mst_topology_state(ctx->state, &ctx->aconnector->mst_mgr); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mst_state); + /* One time slot per 10 PBN keeps the slot counts small and exact. */ + ctx->mst_state->pbn_div.full = dfixed_const(10); + ctx->mst_state->total_avail_slots = total_avail_slots; + /* Needed for the payload limit check; mst_primary stays NULL. */ + ctx->aconnector->mst_mgr.mst_state = true; + + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, res_pool); + KUNIT_ASSERT_NOT_NULL(test, sink); + KUNIT_ASSERT_NOT_NULL(test, port); + + link_srv->mst_decide_link_encoding_format = dm_mst_test_mst_encoding_format; + + dc = dm_kunit_alloc_dc_with_ctx(test); + dc->link_srv = link_srv; + dc->res_pool = res_pool; + dc->current_state = dm_kunit_alloc_dc_state(test); + + link = dm_kunit_alloc_link(test); + link->dc = dc; + link->ctx = dc->ctx; + link->type = dc_connection_mst_branch; + + sink->ctx = dc->ctx; + dm_mst_test_setup_dsc_caps(test, dc, sink); + + /* The payload allocation takes a reference, so start above zero. */ + kref_init(&port->malloc_kref); + port->mgr = &ctx->aconnector->mst_mgr; + port->connector = &ctx->aconnector->base; + port->full_pbn = 2000; + + ctx->aconnector->dc_link = link; + ctx->aconnector->dc_sink = sink; + ctx->aconnector->mst_output_port = port; + + ctx->dc_state = dm_kunit_alloc_dc_state(test); + ctx->stream = dm_kunit_alloc_stream(test, link); + ctx->stream->ctx = dc->ctx; + ctx->stream->sink = sink; + ctx->stream->dm_stream_context = ctx->aconnector; + ctx->stream->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + dm_mst_test_set_dsc_timing(&ctx->stream->timing); + ctx->dc_state->streams[0] = ctx->stream; + ctx->dc_state->stream_count = 1; + + return ctx; +} + +/** + * dm_mst_test_dsc_link_fits_uncompressed - plenty of time slots means no DSC + * @test: KUnit test context + * + * The first pass allocates the uncompressed peak PBN and the MST check + * succeeds, so DSC is left disabled for the stream. + */ +static void dm_mst_test_dsc_link_fits_uncompressed(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 64); + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_FALSE(test, vars[0].dsc_enabled); + KUNIT_EXPECT_EQ(test, (u32)ctx->stream->timing.flags.DSC, 0U); + KUNIT_EXPECT_PTR_EQ(test, vars[0].aconnector, ctx->aconnector); +} + +/** + * dm_mst_test_dsc_link_enables_compression - a tight link is compressed + * @test: KUnit test context + * + * The uncompressed allocation does not fit, so the driver falls back to + * maximum compression and then optimises the bits per pixel back up. DSC ends + * up enabled with a bpp above the policy minimum. + */ +static void dm_mst_test_dsc_link_enables_compression(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 30); + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_TRUE(test, vars[0].dsc_enabled); + KUNIT_EXPECT_GT(test, vars[0].bpp_x16, 0); + KUNIT_EXPECT_EQ(test, (u32)ctx->stream->timing.flags.DSC, 1U); +} + +/** + * dm_mst_test_dsc_link_out_of_slots - even max compression can be too big + * @test: KUnit test context + * + * When the fully compressed stream still exceeds the available time slots the + * -ENOSPC from the MST check is propagated to the caller. + */ +static void dm_mst_test_dsc_link_out_of_slots(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 10); + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), -ENOSPC); +} + +/** + * dm_mst_test_dsc_link_forced_dsc - forced DSC discards the uncompressed pass + * @test: KUnit test context + * + * With DSC forced on from debugfs the uncompressed allocation is not applied + * even though it fits. The MST check returned 0 rather than -ENOSPC, so the + * helper returns that success without programming a DSC config. + */ +static void dm_mst_test_dsc_link_forced_dsc(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 64); + ctx->aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_ENABLE; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_FALSE(test, vars[0].dsc_enabled); + KUNIT_EXPECT_EQ(test, (u32)ctx->stream->timing.flags.DSC, 0U); +} + +/** + * dm_mst_test_dsc_link_forced_dsc_stays_on - forced DSC is never disabled again + * @test: KUnit test context + * + * Once compression is needed, try_disable_dsc() only reconsiders streams left + * at the default DSC clock setting, so a forced stream keeps DSC enabled. + */ +static void dm_mst_test_dsc_link_forced_dsc_stays_on(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 30); + ctx->aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_ENABLE; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_TRUE(test, vars[0].dsc_enabled); +} + +/** + * dm_mst_test_dsc_link_bpp_overwrite - a forced bpp overrides the computed one + * @test: KUnit test context + */ +static void dm_mst_test_dsc_link_bpp_overwrite(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 30); + ctx->aconnector->dsc_settings.dsc_bits_per_pixel = 10 * 16; + ctx->aconnector->dsc_settings.dsc_num_slices_h = 2; + ctx->aconnector->dsc_settings.dsc_num_slices_v = 4; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_EQ(test, ctx->stream->timing.dsc_cfg.bits_per_pixel, 10 * 16); + KUNIT_EXPECT_EQ(test, (u32)ctx->stream->timing.dsc_cfg.num_slices_h, 2U); + KUNIT_EXPECT_EQ(test, (u32)ctx->stream->timing.dsc_cfg.num_slices_v, 4U); +} + +/** + * dm_mst_test_dsc_link_compression_disabled - forcing DSC off keeps it off + * @test: KUnit test context + * + * A stream whose DSC clock is force disabled is allocated its uncompressed + * bandwidth in the max compression pass as well. + */ +static void dm_mst_test_dsc_link_compression_disabled(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 30); + ctx->aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DISABLE; + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), -ENOSPC); +} + +/** + * dm_mst_test_dsc_link_bpp_between_limits - spare slots raise the bits per pixel + * @test: KUnit test context + * + * With only part of the slack available the optimisation loop hands out a fair + * share of the free time slots and derives the resulting bits per pixel from + * the new PBN, landing between the policy minimum and maximum. + */ +static void dm_mst_test_dsc_link_bpp_between_limits(struct kunit *test) +{ + struct dsc_mst_fairness_vars vars[MAX_PIPES] = {}; + struct dm_mst_test_dsc_link_ctx *ctx; + + ctx = dm_mst_test_alloc_dsc_link_ctx(test, 63); + /* Smaller slots leave the fair share below the full slack. */ + ctx->mst_state->pbn_div.full = dfixed_const(4); + + KUNIT_EXPECT_EQ(test, pre_compute_mst_dsc_configs_for_state(ctx->state, ctx->dc_state, vars), 0); + KUNIT_EXPECT_TRUE(test, vars[0].dsc_enabled); + KUNIT_EXPECT_GT(test, vars[0].bpp_x16, 8 * 16); + KUNIT_EXPECT_LT(test, vars[0].bpp_x16, 16 * 16); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3771,6 +4160,19 @@ static struct kunit_case dm_mst_types_test_cases[] = { KUNIT_CASE(dm_mst_test_compute_configs_no_recompute), /* pre_validate_dsc tests */ KUNIT_CASE(dm_mst_test_pre_validate_dsc_not_needed), + /* pre_compute_mst_dsc_configs_for_state tests */ + KUNIT_CASE(dm_mst_test_pre_compute_configs_skips_sst), + KUNIT_CASE(dm_mst_test_pre_compute_configs_skips_incomplete), + KUNIT_CASE(dm_mst_test_pre_compute_configs_no_recompute), + /* compute_mst_dsc_configs_for_link tests */ + KUNIT_CASE(dm_mst_test_dsc_link_fits_uncompressed), + KUNIT_CASE(dm_mst_test_dsc_link_enables_compression), + KUNIT_CASE(dm_mst_test_dsc_link_out_of_slots), + KUNIT_CASE(dm_mst_test_dsc_link_forced_dsc), + KUNIT_CASE(dm_mst_test_dsc_link_forced_dsc_stays_on), + KUNIT_CASE(dm_mst_test_dsc_link_bpp_overwrite), + KUNIT_CASE(dm_mst_test_dsc_link_compression_disabled), + KUNIT_CASE(dm_mst_test_dsc_link_bpp_between_limits), {} }; From 62b29c43aaf2dfec80f9af3d3ca4cf4da9e64b82 Mon Sep 17 00:00:00 2001 From: Wenjing Liu Date: Fri, 28 Aug 2026 13:10:23 -0400 Subject: [PATCH 034/114] drm/amd/display: Add urgent assertion counter probe [Why] Expose continuous urgent assertion counts for memory QoS debugging. [How] Track probe running state and poll the counter through the update path. Reviewed-by: Alvin Lee Signed-off-by: Wenjing Liu Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/core/dc_resource.c | 28 ++++++++-- drivers/gpu/drm/amd/display/dc/dc.h | 2 + .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 54 ++++++++++++++----- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index b3889abab6b6..ecbd2b4a0c22 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -4581,11 +4581,31 @@ enum dc_status resource_validate_probe_set(struct dc *dc, return DC_NOT_SUPPORTED; for (i = 0; i < probe_count; i++) { - if (probes[i].target_state == DC_PROBE_MEASURING) - return DC_NOT_SUPPORTED; - - if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL) + switch (probes[i].type) { + /* These global probes support a one-frame measurement and reset. */ + case DC_PROBE_PEAK_MEM_BW: + case DC_PROBE_PEAK_MEM_BW_STRESSED: + case DC_PROBE_AVG_MEM_BW: + case DC_PROBE_MEM_LATENCY: + case DC_PROBE_PREFETCH_DATA_SIZE: + if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL) + return DC_NOT_SUPPORTED; + if (probes[i].target_state != DC_PROBE_NOT_MEASURING && + probes[i].target_state != DC_PROBE_MEASURED) + return DC_NOT_SUPPORTED; + break; + /* The global urgent assertion counter additionally supports polling. */ + case DC_PROBE_URGENT_ASSERTION_COUNT: + if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL) + return DC_NOT_SUPPORTED; + if (probes[i].target_state != DC_PROBE_NOT_MEASURING && + probes[i].target_state != DC_PROBE_MEASURING && + probes[i].target_state != DC_PROBE_MEASURED) + return DC_NOT_SUPPORTED; + break; + default: return DC_NOT_SUPPORTED; + } if (probes[i].type == DC_PROBE_PEAK_MEM_BW_STRESSED && !dc->res_pool->lsdma_scratch.buffer) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 7ca5355dbad2..ab8c67ad1b50 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -2223,6 +2223,7 @@ struct dc_probe_latencies { /** * struct dc_probe_status - results for a probe. * @valid: true if a measurement was latched. + * @measuring: true if the hardware counter is currently running. * @type: type of the probe that produced this result. * @u.bandwidth_mbps: peak BW in Mbps (DC_PROBE_PEAK_MEM_BW). * @u.latency: min/max/avg memory latency in ns (DC_PROBE_MEM_LATENCY), @@ -2232,6 +2233,7 @@ struct dc_probe_latencies { */ struct dc_probe_status { bool valid; + bool measuring; enum dc_probe_type type; union { uint32_t bandwidth_mbps; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c index 443afcbce5ed..a13d36ad8a9c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c @@ -990,7 +990,7 @@ static struct timing_generator *dcn60_get_ref_tg_for_hubbub_probe( * @dc: DC structure * @context: Committed dc state to resolve streams from * @probe: Probe state to build sequence for - * @status: Perfmon status to update with probe results + * @status: Prior perfmon status, then the result sink for the sequence * @block_sequence: Block sequence to append steps to * @num_steps: Number of steps in the block sequence * @@ -1009,17 +1009,32 @@ static void dcn60_build_hubbub_perfmon_sequence( uint32_t refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; struct timing_generator *ref_tg = dcn60_get_ref_tg_for_hubbub_probe(context); struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps }; + bool was_measuring = status->measuring; if (!hubbub || !hubbub->funcs || !hubbub->funcs->perfmon.reset) return; - status->type = probe->type; - if (probe->target_state == DC_PROBE_NOT_MEASURING) { hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); return; } + if (probe->target_state == DC_PROBE_MEASURING) { + if (probe->type != DC_PROBE_URGENT_ASSERTION_COUNT || + !hubbub->funcs->perfmon.start_measuring_urgent_assertion_count || + !hubbub->funcs->perfmon.get_urgent_assertion_count) + return; + + if (!was_measuring) { + hwss_add_hubbub_perfmon_reset(&seq_state, hubbub); + hwss_add_hubbub_perfmon_start_urgent_assertion_count(&seq_state, hubbub); + } + + hwss_add_hubbub_perfmon_get_urgent_assertion_count(&seq_state, hubbub, + refclk_mhz, &status->u.urgent_assertion_count); + return; + } + if (probe->target_state != DC_PROBE_MEASURED || !ref_tg) return; @@ -1059,7 +1074,7 @@ static void dcn60_build_hubbub_perfmon_sequence( hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE); hwss_add_hubbub_perfmon_get_out_of_order_bw(&seq_state, hubbub, refclk_mhz, &status->u.bandwidth_mbps, NULL); - break; + return; case DC_PROBE_AVG_MEM_BW: /* In-order counter accumulates over a full frame, so no timing group @@ -1076,7 +1091,7 @@ static void dcn60_build_hubbub_perfmon_sequence( hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); hwss_add_hubbub_perfmon_get_in_order_bw(&seq_state, hubbub, refclk_mhz, 0, &status->u.bandwidth_mbps, NULL); - break; + return; case DC_PROBE_MEM_LATENCY: if (!hubbub->funcs->perfmon.start_measuring_memory_latencies || @@ -1091,7 +1106,7 @@ static void dcn60_build_hubbub_perfmon_sequence( hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); hwss_add_hubbub_perfmon_get_memory_latencies(&seq_state, hubbub, refclk_mhz, &status->u.latency); - break; + return; case DC_PROBE_URGENT_ASSERTION_COUNT: if (!hubbub->funcs->perfmon.start_measuring_urgent_assertion_count || @@ -1106,7 +1121,7 @@ static void dcn60_build_hubbub_perfmon_sequence( hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); hwss_add_hubbub_perfmon_get_urgent_assertion_count(&seq_state, hubbub, refclk_mhz, &status->u.urgent_assertion_count); - break; + return; case DC_PROBE_PREFETCH_DATA_SIZE: if (!hubbub->funcs->perfmon.start_measuring_prefetch_data_size || @@ -1121,7 +1136,7 @@ static void dcn60_build_hubbub_perfmon_sequence( hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK); hwss_add_hubbub_perfmon_get_prefetch_data_size(&seq_state, hubbub, &status->u.prefetch_data_size); - break; + return; case DC_PROBE_URGENT_RAMP_LATENCY: /* Requires caller-supplied window params not available in probe model. */ @@ -1136,9 +1151,22 @@ static void dcn60_build_hubbub_perfmon_sequence( * dcn60_update_probe_status - Set the valid flag on a latched probe result. * @status: result sink whose u was written by the GET BLS step during execute */ -static void dcn60_update_probe_status(struct dc_probe_status *status) +static void dcn60_update_probe_status( + struct dc_probe_status *status, + const struct dc_probe_state *probe) { - switch (status->type) { + struct dc_probe_status result = *status; + + memset(status, 0, sizeof(*status)); + + if (probe->target_state == DC_PROBE_NOT_MEASURING) + return; + + status->type = probe->type; + status->measuring = probe->target_state == DC_PROBE_MEASURING; + status->u = result.u; + + switch (probe->type) { case DC_PROBE_PEAK_MEM_BW: case DC_PROBE_PEAK_MEM_BW_STRESSED: case DC_PROBE_AVG_MEM_BW: @@ -1194,11 +1222,11 @@ void dcn60_program_perfmon(struct dc *dc, struct dc_state *context) return; context->block_sequence_steps = 0; - memset(context->probe_status, 0, sizeof(context->probe_status)); for (i = 0; i < context->probe_count; i++) { if (is_probe_measurement_type_for_hubbub(context->probes[i].type)) - dcn60_build_hubbub_perfmon_sequence(dc, context, &context->probes[i], + dcn60_build_hubbub_perfmon_sequence(dc, context, + &context->probes[i], &context->probe_status[i], context->block_sequence, &context->block_sequence_steps); @@ -1207,7 +1235,7 @@ void dcn60_program_perfmon(struct dc *dc, struct dc_state *context) hwss_execute_sequence(dc, context->block_sequence, context->block_sequence_steps); for (i = 0; i < context->probe_count; i++) - dcn60_update_probe_status(&context->probe_status[i]); + dcn60_update_probe_status(&context->probe_status[i], &context->probes[i]); } static bool dcn60_has_active_memory_request(const struct dc *dc) From 34a429d37b62ddf748251fcc012cd3cf80b58040 Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Tue, 1 Sep 2026 12:04:23 -0400 Subject: [PATCH 035/114] drm/amd/display: Add debug option to force optional UCLK support [Description] Add debug option to force optional uclk support Reviewed-by: Wenjing Liu Signed-off-by: Alvin Lee Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 1 + .../dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index ab8c67ad1b50..1e25ed4eea80 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1288,6 +1288,7 @@ struct dc_debug_options { bool dml21_force_pstate_method; uint32_t dml21_force_pstate_method_values[MAX_PIPES]; uint32_t dml21_disable_pstate_method_mask; + bool force_optional_uclk_pstate_support; union fw_assisted_mclk_switch_version fams_version; union dmub_fams2_global_feature_config fams2_config; bool fams2_imm_restore_drr; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c index 3c25de54d55b..366767f4413e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c @@ -39,6 +39,7 @@ static void dml21_populate_pmo_options(struct dml2_pmo_options *pmo_options, pmo_options->disable_drr_clamped_when_var_active = in_dc->debug.disable_fams_gaming == INGAME_FAMS_DISABLE; pmo_options->force_mandatory_uclk_pstate_support = config->pmo.force_mandatory_uclk_pstate_support; + pmo_options->force_optional_uclk_pstate_support = in_dc->debug.force_optional_uclk_pstate_support; } static enum dml2_project_id dml21_dcn_revision_to_dml2_project_id(const struct dc *in_dc) From 5bfc935af5352b8db7a512c3cb97c9fe76ca0618 Mon Sep 17 00:00:00 2001 From: Ivan Lipski Date: Tue, 1 Sep 2026 15:39:11 -0400 Subject: [PATCH 036/114] drm/amd/display: Honor forced RGB pixel encoding [Why&How] Forcing RGB format via debugfs on a monitor supporting YUV444 is ignored. Similarly to YUV format forcing, only use RGB pixel encoding if it is specified in the force_yuv_pixel_formats debugfs. Reviewed-by: Jerry Zuo Reviewed-by: Leo Li Signed-off-by: Ivan Lipski Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index eba77a6f50e2..fd5bb9004f6c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -2326,7 +2326,9 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, * (drm_mode_is_420_also() clear), as required for HDMI compliance * testing; dc_validate_stream() still rejects anything the link * genuinely cannot carry. The YCbCr422/YCbCr444 forces stay gated on - * the sink's advertised caps. + * the sink's advertised caps. An RGB force pins to RGB alone and is + * always honoured, since RGB is the mandatory baseline every sink + * (DP or HDMI) supports. */ want_420 = (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); @@ -2346,7 +2348,8 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && is_dp_or_hdmi) { encoding_mask = BIT(PIXEL_ENCODING_YCBCR444); - } else if (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444) { + } else if ((aconnector->force_yuv_pixel_format == PIXEL_ENCODING_RGB) || + (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444)) { encoding_mask = BIT(PIXEL_ENCODING_RGB); } else { encoding_mask = BIT(PIXEL_ENCODING_RGB); From b019bd7ea5bc5b3f9d221763ca547e279db4ee28 Mon Sep 17 00:00:00 2001 From: Wei-Guang Li Date: Tue, 1 Sep 2026 15:35:50 +0800 Subject: [PATCH 037/114] drm/amd/display: Add Replay cumulative residency query [Why&How] Add driver-side support for querying boot-time cumulative PHY-off residency via the REPLAY_SNAPSHOT_CUMULATIVE_RESIDENCY GPINT command implemented in DMUB FW. Reviewed-by: Jack Chang Signed-off-by: Wei-Guang Li Signed-off-by: Jack Chang Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/dce/dmub_replay.c | 25 +++++++++++++++++++ .../gpu/drm/amd/display/dc/dce/dmub_replay.h | 2 ++ .../gpu/drm/amd/display/dc/inc/link_service.h | 2 ++ .../drm/amd/display/dc/link/link_factory.c | 1 + .../link/protocols/link_edp_panel_control.c | 22 ++++++++++++++++ .../link/protocols/link_edp_panel_control.h | 2 ++ .../drm/amd/display/modules/inc/mod_power.h | 3 +++ .../amd/display/modules/power/power_replay.c | 13 ++++++++++ 8 files changed, 70 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c index eda3a2f1402c..0785a30eb643 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c @@ -415,6 +415,30 @@ static void dmub_replay_send_cmd(struct dmub_replay *dmub, dc_wake_and_execute_dmub_cmd(ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); } +static bool dmub_replay_get_cumulative_residency(struct dmub_replay *dmub, + uint8_t panel_inst, uint32_t *residency_milli_pct) +{ + uint32_t milli_pct = 0; + uint16_t param = (uint16_t)panel_inst; + + if (!residency_milli_pct) + return false; + + /* Zero on failure so the caller never reads a stale value, and report + * false so a failed query is still distinguishable from a genuine 0%. + */ + *residency_milli_pct = 0; + + if (!dc_wake_and_execute_gpint(dmub->ctx, + DMUB_GPINT__REPLAY_SNAPSHOT_CUMULATIVE_RESIDENCY, + param, &milli_pct, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)) + return false; + + *residency_milli_pct = milli_pct; + + return true; +} + static const struct dmub_replay_funcs replay_funcs = { .replay_copy_settings = dmub_replay_copy_settings, .replay_enable = dmub_replay_enable, @@ -424,6 +448,7 @@ static const struct dmub_replay_funcs replay_funcs = { .replay_residency = dmub_replay_residency, .replay_set_power_opt_and_coasting_vtotal = dmub_replay_set_power_opt_and_coasting_vtotal, .replay_send_cmd = dmub_replay_send_cmd, + .replay_get_cumulative_residency = dmub_replay_get_cumulative_residency, }; /* diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h index 07c79739a980..271a42a226e9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h @@ -33,6 +33,8 @@ struct dmub_replay_funcs { void (*replay_set_power_opt_and_coasting_vtotal)(struct dmub_replay *dmub, unsigned int power_opt, uint8_t panel_inst, uint32_t coasting_vtotal, uint16_t frame_skip_number); + bool (*replay_get_cumulative_residency)(struct dmub_replay *dmub, + uint8_t panel_inst, uint32_t *residency_milli_pct); }; struct dmub_replay *dmub_replay_create(struct dc_context *ctx); diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h index eaf20ca14bcc..b2f86c51d470 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h +++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h @@ -297,6 +297,8 @@ struct link_service { bool (*edp_replay_residency)(const struct dc_link *link, unsigned int *residency, const bool is_start, const enum pr_residency_mode mode); + bool (*edp_replay_get_cumulative_residency)(const struct dc_link *link, + uint32_t *residency_milli_pct); bool (*edp_set_replay_power_opt_and_coasting_vtotal)(struct dc_link *link, const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number); diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index fa281de1d566..3b192c539f8b 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -223,6 +223,7 @@ static void construct_link_service_edp_panel_control(struct link_service *link_s link_srv->edp_send_replay_cmd = edp_send_replay_cmd; link_srv->edp_set_coasting_vtotal = edp_set_coasting_vtotal; link_srv->edp_replay_residency = edp_replay_residency; + link_srv->edp_replay_get_cumulative_residency = edp_replay_get_cumulative_residency; link_srv->edp_set_replay_power_opt_and_coasting_vtotal = edp_set_replay_power_opt_and_coasting_vtotal; link_srv->edp_wait_for_t12 = edp_wait_for_t12; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 9883216dcc9d..e904c7dd320e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -1154,6 +1154,28 @@ bool edp_replay_residency(const struct dc_link *link, return true; } +bool edp_replay_get_cumulative_residency(const struct dc_link *link, + uint32_t *residency_milli_pct) +{ + struct dc *dc = link->ctx->dc; + struct dmub_replay *replay = dc->res_pool->replay; + unsigned int panel_inst; + + if (!dp_pr_get_pr_panel_inst(dc, link, &panel_inst)) + return false; + + if (replay && link->replay_settings.replay_feature_enabled && + replay->funcs->replay_get_cumulative_residency) + return replay->funcs->replay_get_cumulative_residency(replay, + (uint8_t)panel_inst, residency_milli_pct); + + if (residency_milli_pct) + *residency_milli_pct = 0; + + return true; +} + + bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link, const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number) { diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h index 51de13e2387e..0082f5e91878 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h @@ -60,6 +60,8 @@ bool edp_send_replay_cmd(struct dc_link *link, bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal, uint16_t frame_skip_number); bool edp_replay_residency(const struct dc_link *link, unsigned int *residency, const bool is_start, const enum pr_residency_mode mode); +bool edp_replay_get_cumulative_residency(const struct dc_link *link, + uint32_t *residency_milli_pct); bool edp_get_replay_state(const struct dc_link *link, uint64_t *state); bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link, const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number); diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_power.h b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h index 5d1275b7cd46..25a0c96ac754 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_power.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h @@ -375,6 +375,9 @@ bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power, void mod_power_replay_residency(const struct dc_stream_state *stream, unsigned int *residency, const bool is_start, const bool is_alpm); +bool mod_power_replay_get_cumulative_residency(const struct dc_stream_state *stream, + uint32_t *residency_milli_pct); + bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, bool is_ultra_sleep_mode, uint16_t frame_skip_number); diff --git a/drivers/gpu/drm/amd/display/modules/power/power_replay.c b/drivers/gpu/drm/amd/display/modules/power/power_replay.c index 31d9cb3a12ba..7861e46c612d 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_replay.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c @@ -761,6 +761,19 @@ void mod_power_replay_residency(const struct dc_stream_state *stream, link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode); } +bool mod_power_replay_get_cumulative_residency(const struct dc_stream_state *stream, + uint32_t *residency_milli_pct) +{ + const struct dc_link *link = dc_stream_get_link(stream); + + if (!link || !link->dc || !link->dc->link_srv) + return false; + + return link->dc->link_srv->edp_replay_get_cumulative_residency( + link, residency_milli_pct); +} + + bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, bool is_ultra_sleep_mode, uint16_t frame_skip_number) From cd53dcd2a7d733162d8e23472d65995ca0b58902 Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Tue, 1 Sep 2026 15:45:54 -0400 Subject: [PATCH 038/114] drm/amd/display: Force DSC to 8bpp for MST DP tunneling over USB4 Commit "drm/amd/display: Force DSC to 8bpp for SST DP tunneling over USB4" forces DSC and caps the target to 8 bpp for SST streams on a USB4 DPIA link with bandwidth allocation enabled. The MST DSC fairness path does not honor that policy: it derives the bpp range purely from the timing's DSC policy and picks the highest bpp the native DP link can carry, which overflows the smaller USB4 tunnel budget on a dock that fans out via an internal MST hub. Apply the equivalent policy in compute_mst_dsc_configs_for_link(): when the link is a USB4 DPIA with BW allocation enabled, force DSC on (so the fairness pass does not leave a stream uncompressed just because the native link looks large enough) and pin the target to the minimum bpp (so the compressed stream fits the tunnel and reserves headroom for the other tunnels on the same host router). Reviewed-by: Roman Li Signed-off-by: Fangzhi Zuo Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index b6b7fd7f8bca..a5b15bef8aa0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1375,6 +1375,10 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state, params[count].num_slices_v = aconnector->dsc_settings.dsc_num_slices_v; params[count].bpp_overwrite = aconnector->dsc_settings.dsc_bits_per_pixel; params[count].compression_possible = stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported; + if (params[count].compression_possible && + dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + dc_link->dpia_bw_alloc_config.bw_alloc_enabled) + params[count].clock_force_enable = DSC_CLK_FORCE_ENABLE; dc_dsc_get_policy_for_timing(params[count].timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link)); is_frl_endpoint_present = get_conv_frl_bw(aconnector, &frl_conv_bw_in_kbps, &frl_conv_dsc_bw_in_kbps); if (stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported && @@ -1386,7 +1390,10 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state, stream->sink->ctx->dc->res_pool->dscs[0], stream->sink->ctx->dc->debug.dsc_min_slice_height_override, dsc_policy.min_target_bpp * 16, - dsc_policy.max_target_bpp * 16, + (dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + dc_link->dpia_bw_alloc_config.bw_alloc_enabled) ? + dsc_policy.min_target_bpp * 16 : + dsc_policy.max_target_bpp * 16, &stream->sink->dsc_caps.dsc_dec_caps, &stream->timing, dc_link_get_highest_encoding_format(dc_link), From c30b9813bce1d9e2361dc80ef40208b045bde8ca Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Tue, 1 Sep 2026 15:32:30 -0400 Subject: [PATCH 039/114] drm/amd/display: Force DSC to 8bpp for SST DP tunneling over USB4 A 4K144 display attached through a USB4 (Thunderbolt) dock does not light up on DCN4.2. The native DP link bandwidth reported for the sink is far larger than the USB4 tunnel can carry, so DSC is sized to the link and the resulting stream overflows the tunnel's granted bandwidth. The bandwidth allocation request fails and the display stays dark. When the endpoint is a USB4 DPIA with BW allocation enabled, force DSC and cap the target to 8 bpp in SST mode. This keeps the compressed stream within the tunnel budget and reserves headroom for other DP tunnels sharing the same host router. Reviewed-by: Roman Li Signed-off-by: Fangzhi Zuo Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index fd5bb9004f6c..31e9f029a707 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -1267,6 +1267,12 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector, link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link, dc_link_get_link_cap(aconnector->dc_link)); + if (aconnector->dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + aconnector->dc_link->dpia_bw_alloc_config.bw_alloc_enabled) { + dsc_options.max_target_bpp_limit_override_x16 = 8 * 16; + dsc_options.force_dsc_when_not_needed = true; + } + /* Set DSC policy according to dsc_clock_en */ dc_dsc_policy_set_enable_dsc_when_not_needed( aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE); From 3a38773e090639f8377032bae8c942baaafead7d Mon Sep 17 00:00:00 2001 From: Wenjing Liu Date: Tue, 1 Sep 2026 00:41:16 -0400 Subject: [PATCH 040/114] drm/amd/display: Fix peak bandwidth measurement sequence [Why] Peak bandwidth samples could intermittently report invalid counter values because completion and stale stop events were not isolated. The default measurement window also made completion less likely within the available sampling period. [How] Clear indirect counter state while masking stop output, use one counter as the sole measurement completion source, and shorten the default measurement window to 5000 events. Reviewed-by: Alvin Lee Signed-off-by: Wenjing Liu Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/dc/hubbub/dcn60/dcn60_hubbub.c | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c index 0169ffcd9343..c89c8ff42185 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c @@ -742,9 +742,19 @@ static void hubbub60_wm_read_state(struct hubbub *hubbub, static void hubbub60_perfmon_reset(struct hubbub *hubbub) { struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + unsigned int counter; REG_WRITE(DC_PERFMON5_PERFMON_CNTL, 0); REG_WRITE(DC_PERFMON5_PERFMON_CNTL2, 0); + + for (counter = 0; counter < 8; counter++) { + REG_SET_2(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + PERFCOUNTER_CNTL_SEL, counter, + PERFCOUNTER_OFF_MASK, 0x1); + REG_SET(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, + PERFCOUNTER_CNTL2_SEL, counter); + } + REG_WRITE(DC_PERFMON5_PERFCOUNTER_STATE, 0); REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, 0xFF00); REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_LOW, 0); @@ -1381,7 +1391,7 @@ static uint32_t hubbub60_perfmon_get_urgent_ramp_latency_ns( * Counter 1: data counter — tracks total data received during the measurement * period. * Counter 2: target-reached generator — duplicates counter 1's event/target - * and generates the interrupt that stops counters 1 and 4. + * and generates the counter-off event that stops counters 1 and 4. * Counter 4: duration timer — measures elapsed time in refclk cycles. * * UTM_FILTER_SEL is set to 0 so that isolation comes from OTG-vblank gating @@ -1396,7 +1406,7 @@ static uint32_t hubbub60_perfmon_get_urgent_ramp_latency_ns( * windbg (ed dcn60_debug_peak_bw_ramp_size / ed dcn60_debug_peak_bw_window_size). */ static unsigned int dcn60_debug_peak_bw_ramp_size = 0x100; -static unsigned int dcn60_debug_peak_bw_window_size = 0x2500; +static unsigned int dcn60_debug_peak_bw_window_size = 5000; /* Minimum count4 (refclk ticks) for a peak-BW sample to be trusted. */ #define DCN60_OUT_OF_ORDER_BW_MIN_DURATION_TICKS 20 @@ -1419,7 +1429,7 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( UTM_FILTER_SEL, 0x0); /* Program counter 0 as the count off counter */ - REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0 PERFCOUNTER_EVENT_SEL, 259, // response vld PERFCOUNTER_CVALUE_SEL, 0x1, // use cvalue bits 15-0 @@ -1427,7 +1437,8 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done - PERFCOUNTER_ACTIVE, 1); + PERFCOUNTER_OFF_MASK, 0x1, // do not generate pmon_counter_off_out + PERFCOUNTER_ACTIVE, 0x1); REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0 PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value @@ -1436,7 +1447,7 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( /* Program counter 1 as a passive data accumulator, slaved to the same * external stop signal as counter 4 (generated by counter 2 below). */ - REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + REG_SET_10(DC_PERFMON5_PERFCOUNTER_CNTL, 0, PERFCOUNTER_CNTL_SEL, 0x1, // select counter 1 PERFCOUNTER_EVENT_SEL, 259, // response vld PERFCOUNTER_CVALUE_SEL, 0x2, // use cvalue bits 31-16 @@ -1444,7 +1455,8 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done - PERFCOUNTER_INT_EN, 0, // counter 2 now generates the interrupt + PERFCOUNTER_INT_EN, 0, // counter 2 is the completion source + PERFCOUNTER_OFF_MASK, 0x1, // do not generate pmon_counter_off_out PERFCOUNTER_ACTIVE, 0x1); REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, PERFCOUNTER_CNTL2_SEL, 0x1, @@ -1454,9 +1466,9 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_STOP2_SEL, 0x1); // stop via the same external signal as counter 4 /* Counter 2 is a dedicated "target reached" generator: it duplicates - * counter 1's event/target and is the sole source of the completion - * interrupt driving PERFMON_RUN_ENABLE_STOP_SEL. */ - REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + * counter 1's event/target and is the sole source of the counter-off + * event driving PERFMON_RUN_ENABLE_STOP_SEL. */ + REG_SET_10(DC_PERFMON5_PERFCOUNTER_CNTL, 0, PERFCOUNTER_CNTL_SEL, 0x2, // select counter 2 PERFCOUNTER_EVENT_SEL, 259, // response vld PERFCOUNTER_CVALUE_SEL, 0x2, // use cvalue bits 31-16 (same window target as counter 1) @@ -1464,7 +1476,8 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done - PERFCOUNTER_INT_EN, 1, // signal when the measurement is complete + PERFCOUNTER_INT_EN, 0, // counter-off is the completion source + PERFCOUNTER_OFF_MASK, 0x0, // generate pmon_counter_off_out at CVALUE PERFCOUNTER_ACTIVE, 0x1); REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, PERFCOUNTER_CNTL2_SEL, 0x2, @@ -1474,7 +1487,7 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_STOP2_SEL, 0x0); // the stop trigger is that perfcounter meet the target CVALUE /* Program counter 4 to count the measuring time */ - REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0, + REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0, PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4 PERFCOUNTER_EVENT_SEL, 19, // always 1 event PERFCOUNTER_CVALUE_SEL, 0x0, // ignored @@ -1482,6 +1495,7 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done + PERFCOUNTER_OFF_MASK, 0x1, // do not generate pmon_counter_off_out PERFCOUNTER_ACTIVE, 1); REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0, PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4 @@ -1509,7 +1523,7 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0, PERFMON_RUN_ENABLE_START_SEL, 0x0, - PERFMON_RUN_ENABLE_STOP_SEL, 11); // perfmon counter off event + PERFMON_RUN_ENABLE_STOP_SEL, 10); // stop on counter-off event } /** @@ -1517,9 +1531,9 @@ static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth( * @hubbub: pointer to the hubbub hardware instance * * Enables the performance monitor counters previously configured by - * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth(). The counter - * self-stops once the count-off counter reaches its target; there is no - * explicit stop step for the peak-BW path. + * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth(). Counter 2 stops + * the measurement once it reaches its target; there is no explicit stop + * step for the peak-BW path. */ static void hubbub60_perfmon_start_measuring_out_of_order_bandwidth( struct hubbub *hubbub) From bd0d24efea9d18eee0d4e530b7bd0263df7b4ebb Mon Sep 17 00:00:00 2001 From: Tony Cheng Date: Tue, 1 Sep 2026 13:42:48 -0400 Subject: [PATCH 041/114] drm/amd/display: Add instance field to struct mpc [Why] struct mpc has no instance identifier, unlike other DC hardware blocks. Debug and tracing helpers need one to identify the block. [How] Add an inst member to struct mpc and initialize it to 0 in every dcnXX_mpc_construct(), since there is one MPC instance per ASIC. Reviewed-by: Jun Lei Signed-off-by: Tony Cheng Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c | 1 + drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c | 1 + 8 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h index d19f90a5893e..75ebfab87efc 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h @@ -281,6 +281,7 @@ struct mpc_tree { struct mpc { const struct mpc_funcs *funcs; struct dc_context *ctx; + int inst; struct mpcc mpcc_array[MAX_MPCC]; struct pwl_params blender_params; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c index 0f2294bedcc3..f9bd1d6e1642 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c @@ -503,6 +503,7 @@ void dcn10_mpc_construct(struct dcn10_mpc *mpc10, int i; mpc10->base.ctx = ctx; + mpc10->base.inst = 0; mpc10->base.funcs = &dcn10_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c index 0e09d073ab29..905d4f23f92a 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c @@ -588,6 +588,7 @@ void dcn20_mpc_construct(struct dcn20_mpc *mpc20, int i; mpc20->base.ctx = ctx; + mpc20->base.inst = 0; mpc20->base.funcs = &dcn20_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c index 9cc6fd38da19..f3213ecb1745 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c @@ -1589,6 +1589,7 @@ void dcn30_mpc_construct(struct dcn30_mpc *mpc30, int i; mpc30->base.ctx = ctx; + mpc30->base.inst = 0; mpc30->base.funcs = &dcn30_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c index 1f15ada109b6..61a1e3f06575 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c @@ -1036,6 +1036,7 @@ void dcn32_mpc_construct(struct dcn30_mpc *mpc30, int i; mpc30->base.ctx = ctx; + mpc30->base.inst = 0; mpc30->base.funcs = &dcn32_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c index df92d8008fe6..e59454090691 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c @@ -680,6 +680,7 @@ void dcn401_mpc_construct(struct dcn401_mpc *mpc401, int i; mpc401->base.ctx = ctx; + mpc401->base.inst = 0; mpc401->base.funcs = &dcn401_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c index 0123a9fcfef5..37d024cfb3fb 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn42/dcn42_mpc.c @@ -121,6 +121,7 @@ void dcn42_mpc_construct(struct dcn42_mpc *mpc42, int i; mpc42->base.ctx = ctx; + mpc42->base.inst = 0; mpc42->base.funcs = &dcn42_mpc_funcs; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c index ae6f2f1b7eca..e38582f0a319 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn60/dcn60_mpc.c @@ -273,6 +273,7 @@ void dcn60_mpc_construct(struct dcn60_mpc *mpc60, int i; mpc60->base.ctx = ctx; + mpc60->base.inst = 0; mpc60->base.funcs = &dcn60_mpc_funcs; From b737b18b25076fa73815ad846a2416153118ab06 Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Wed, 2 Sep 2026 11:49:55 -0400 Subject: [PATCH 042/114] drm/amd/display: Enable back alt-ch [Description] Enable back alt-ch support since external dependencies are ready. Also enable workaround path for better debug capability. Reviewed-by: Dillon Varone Signed-off-by: Alvin Lee Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c index 89d9ef5e7857..171626298e41 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c @@ -670,11 +670,10 @@ static const struct dc_debug_options debug_defaults_drv = { .enable_ppt_check = true, .enable_offload_flip = true, .enable_stall_recovery = true, + .alternate_channel_workaround = true, } }, .force_cositing = CHROMA_COSITING_NONE + 1, - .dml21_disable_pstate_method_mask = 0x20, // disable alt-ch unconditionally until dependencies are ready - }; static const struct dc_check_config config_defaults = { From 20d78472ee2eb3981f37c1dd673af5b8f64245fd Mon Sep 17 00:00:00 2001 From: Tomasz Siemek Date: Wed, 2 Sep 2026 20:40:41 +0200 Subject: [PATCH 043/114] drm/amd/display: Decouple HUBP_UPDATE_PLANE_ADDR from pipe_ctx [Why] Every HWSS Block Level Sequence operation should have flat parameter list and an explicit instance pointer. Global state should not be accessed at execute time. HUBP_UPDATE_PLANE_ADDR does not adhere. It stashes pipe_ctx in its params. Pipe context is mutated during execution. [How] - Adjust HUBP_UPDATE_PLANE_ADDR params to hubp_program_surface_flip_and_addr parameter list. - Add a build-phase hwss vtable entry - prepare_plane_addr_update that does parameter preparation and all global state mutation. - Execute hubp_program_surface_flip_and_addr with precalculated parameters during execution phase. Reviewed-by: Rafal Ostrowski Signed-off-by: Tomasz Siemek Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/core/dc_hw_sequencer.c | 26 ++++++++++++------- .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 25 ++++++++++++++---- .../amd/display/dc/hwss/dcn10/dcn10_hwseq.h | 2 ++ .../amd/display/dc/hwss/dcn10/dcn10_init.c | 1 + .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 25 ++++++++++++++---- .../amd/display/dc/hwss/dcn20/dcn20_hwseq.h | 2 ++ .../amd/display/dc/hwss/dcn20/dcn20_init.c | 1 + .../amd/display/dc/hwss/dcn201/dcn201_hwseq.c | 26 +++++++++++++++---- .../amd/display/dc/hwss/dcn201/dcn201_hwseq.h | 2 ++ .../amd/display/dc/hwss/dcn201/dcn201_init.c | 1 + .../amd/display/dc/hwss/dcn21/dcn21_init.c | 1 + .../amd/display/dc/hwss/dcn30/dcn30_init.c | 1 + .../amd/display/dc/hwss/dcn301/dcn301_init.c | 1 + .../amd/display/dc/hwss/dcn31/dcn31_init.c | 1 + .../amd/display/dc/hwss/dcn314/dcn314_init.c | 1 + .../amd/display/dc/hwss/dcn32/dcn32_init.c | 1 + .../amd/display/dc/hwss/dcn35/dcn35_init.c | 1 + .../amd/display/dc/hwss/dcn351/dcn351_init.c | 1 + .../amd/display/dc/hwss/dcn401/dcn401_init.c | 1 + .../amd/display/dc/hwss/dcn42/dcn42_init.c | 1 + .../amd/display/dc/hwss/dcn60/dcn60_init.c | 1 + .../drm/amd/display/dc/hwss/hw_sequencer.h | 9 +++++-- 22 files changed, 105 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 7262596c470e..ccc58d42e0ab 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -1471,7 +1471,7 @@ void hwss_build_fast_sequence(struct dc *dc, block_sequence[*num_steps].func = HUBP_PROGRAM_TRIPLEBUFFER; (*num_steps)++; } - if (dc->hwss.update_plane_addr && current_mpc_pipe->plane_state->update_bits.addr_update) { + if (dc->hwss.prepare_plane_addr_update && current_mpc_pipe->plane_state->update_bits.addr_update) { if (resource_is_pipe_type(current_mpc_pipe, OTG_MASTER) && stream_status->mall_stream_config.type == SUBVP_MAIN) { block_sequence[*num_steps].params.subvp_save_surf_addr.dc_dmub_srv = dc->ctx->dmub_srv; @@ -1481,10 +1481,7 @@ void hwss_build_fast_sequence(struct dc *dc, (*num_steps)++; } - block_sequence[*num_steps].params.update_plane_addr_params.dc = dc; - block_sequence[*num_steps].params.update_plane_addr_params.pipe_ctx = current_mpc_pipe; - block_sequence[*num_steps].func = HUBP_UPDATE_PLANE_ADDR; - (*num_steps)++; + hwss_add_hubp_update_plane_addr(&seq_state, dc, current_mpc_pipe); } if (current_mpc_pipe->plane_state->update_bits.lut_3d && @@ -1713,8 +1710,10 @@ void hwss_execute_sequence(struct dc *dc, params->program_triplebuffer_params.enableTripleBuffer); break; case HUBP_UPDATE_PLANE_ADDR: - dc->hwss.update_plane_addr(params->update_plane_addr_params.dc, - params->update_plane_addr_params.pipe_ctx); + params->update_plane_addr_params.hubp->funcs->hubp_program_surface_flip_and_addr( + params->update_plane_addr_params.hubp, + ¶ms->update_plane_addr_params.address, + params->update_plane_addr_params.flip_immediate); break; case DPP_SET_INPUT_TRANSFER_FUNC: hws->funcs.set_input_transfer_func(¶ms->set_input_transfer_func_params); @@ -2345,9 +2344,18 @@ void hwss_add_hubp_update_plane_addr(struct block_sequence_state *seq_state, struct dc *dc, struct pipe_ctx *pipe_ctx) { + struct hubp *hubp = pipe_ctx->plane_res.hubp; + + if (!dc->hwss.prepare_plane_addr_update || !hubp || !hubp->funcs->hubp_program_surface_flip_and_addr) + return; + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { - seq_state->steps[*seq_state->num_steps].params.update_plane_addr_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.update_plane_addr_params.pipe_ctx = pipe_ctx; + struct update_plane_addr_params *params = + &seq_state->steps[*seq_state->num_steps].params.update_plane_addr_params; + + dc->hwss.prepare_plane_addr_update(dc, pipe_ctx, + ¶ms->address, ¶ms->flip_immediate); + params->hubp = hubp; seq_state->steps[*seq_state->num_steps].func = HUBP_UPDATE_PLANE_ADDR; (*seq_state->num_steps)++; } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index fefc9dea7054..270b6f2d4377 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -2060,7 +2060,8 @@ static bool patch_address_for_sbs_tb_stereo( return false; } -void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate) { (void)dc; bool addr_patched = false; @@ -2072,10 +2073,8 @@ void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr); - pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( - pipe_ctx->plane_res.hubp, - &plane_state->address, - plane_state->flip_immediate); + *addr_to_program = plane_state->address; + *flip_immediate = plane_state->flip_immediate; plane_state->status.requested_address = plane_state->address; @@ -2086,6 +2085,22 @@ void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; } +void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_address address; + bool flip_immediate; + + if (pipe_ctx->plane_state == NULL) + return; + + dcn10_prepare_plane_addr_update(dc, pipe_ctx, &address, &flip_immediate); + + pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( + pipe_ctx->plane_res.hubp, + &address, + flip_immediate); +} + bool dcn10_set_input_transfer_func(struct set_input_transfer_func_params *params) { struct dpp *dpp = params->dpp; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h index 30c213f2e8d6..5cb109040d3d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h @@ -73,6 +73,8 @@ void dcn10_program_output_csc(struct dc *dc, bool dcn10_set_output_transfer_func(struct set_output_transfer_func_params *params); bool dcn10_set_input_transfer_func(struct set_input_transfer_func_params *params); void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx); +void dcn10_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate); void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn10_reset_hw_ctx_wrap( struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.c index 47ab50a3fab1..07e92742c11a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.c @@ -39,6 +39,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = { .post_unlock_program_front_end = dcn10_post_unlock_program_front_end, .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .update_plane_addr = dcn10_update_plane_addr, + .prepare_plane_addr_update = dcn10_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .clear_surface_dcc_and_tiling = dcn10_reset_surface_dcc_and_tiling, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 0998106c1da0..a3d2385bc731 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -2777,7 +2777,8 @@ static bool patch_address_for_sbs_tb_stereo( return false; } -void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn20_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate) { bool addr_patched = false; PHYSICAL_ADDRESS_LOC addr; @@ -2792,10 +2793,8 @@ void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) vm_helper_mark_vmid_used(dc->vm_helper, plane_state->address.vmid, (uint8_t)pipe_ctx->plane_res.hubp->inst); - pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( - pipe_ctx->plane_res.hubp, - &plane_state->address, - plane_state->flip_immediate); + *addr_to_program = plane_state->address; + *flip_immediate = plane_state->flip_immediate; plane_state->status.requested_address = plane_state->address; @@ -2806,6 +2805,22 @@ void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; } +void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_address address; + bool flip_immediate; + + if (pipe_ctx->plane_state == NULL) + return; + + dcn20_prepare_plane_addr_update(dc, pipe_ctx, &address, &flip_immediate); + + pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( + pipe_ctx->plane_res.hubp, + &address, + flip_immediate); +} + void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, struct dc_link_settings *link_settings) { diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h index c7bf79fda54c..9711b8bc9083 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h @@ -41,6 +41,8 @@ void dcn20_post_unlock_program_front_end( struct dc *dc, struct dc_state *context); void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx); +void dcn20_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate); void dcn20_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx); bool dcn20_set_input_transfer_func(struct set_input_transfer_func_params *params); bool dcn20_set_output_transfer_func(struct set_output_transfer_func_params *params); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.c index 1c6c4e19bdda..c4de9cf11c23 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.c @@ -40,6 +40,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c index 9f4bc2a49873..916b6804e8e6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c @@ -133,7 +133,8 @@ static void plane_address_in_gpu_space_to_uma(struct dce_hwseq *hwseq, } } -void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn201_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate) { bool addr_patched = false; PHYSICAL_ADDRESS_LOC addr; @@ -149,10 +150,9 @@ void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) plane_address_in_gpu_space_to_uma(hws, &uma); - pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( - pipe_ctx->plane_res.hubp, - &uma, - plane_state->flip_immediate); + /* snapshot the UMA-converted address before the stereo patch is restored */ + *addr_to_program = uma; + *flip_immediate = plane_state->flip_immediate; plane_state->status.requested_address = plane_state->address; @@ -163,6 +163,22 @@ void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; } +void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_address address; + bool flip_immediate; + + if (pipe_ctx->plane_state == NULL) + return; + + dcn201_prepare_plane_addr_update(dc, pipe_ctx, &address, &flip_immediate); + + pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( + pipe_ctx->plane_res.hubp, + &address, + flip_immediate); +} + /* Blank pixel data during initialization */ void dcn201_init_blank( struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.h index bab5d9e6912e..dbcaa0a3a02c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.h @@ -33,6 +33,8 @@ void dcn201_init_hw(struct dc *dc); void dcn201_unblank_stream(struct pipe_ctx *pipe_ctx, struct dc_link_settings *link_settings); void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx); +void dcn201_prepare_plane_addr_update(const struct dc *dc, struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, bool *flip_immediate); void dcn201_plane_atomic_disconnect(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx); void dcn201_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn201_set_cursor_attribute(struct pipe_ctx *pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.c index 95dda06b6b17..be1fb2366c8a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.c @@ -41,6 +41,7 @@ static const struct hw_sequencer_funcs dcn201_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn10_post_unlock_program_front_end, .update_plane_addr = dcn201_update_plane_addr, + .prepare_plane_addr_update = dcn201_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.c index b8ed664e1bab..20c6e7d7c897 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.c @@ -41,6 +41,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c index b655c01f013b..f62f1c6efff8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c @@ -41,6 +41,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.c index dfacc59166cd..a62434138eca 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.c @@ -43,6 +43,7 @@ static const struct hw_sequencer_funcs dcn301_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c index 3c2e2162da11..30ad41d08d6e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c @@ -44,6 +44,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c index b50ab468b8f8..a53e7fb6f23f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c @@ -46,6 +46,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c index 225128962216..7e68029f1e12 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c @@ -44,6 +44,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c index c9125cc25d15..69fa91aeb836 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c @@ -48,6 +48,7 @@ static const struct hw_sequencer_funcs dcn35_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c index e6f7d57cbfed..3ceaa4590148 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c @@ -47,6 +47,7 @@ static const struct hw_sequencer_funcs dcn351_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn20_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c index 783415e4e3a1..5ac79c32ee48 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c @@ -23,6 +23,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn401_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c index b074aa9a21d9..faeb365dbcab 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c @@ -26,6 +26,7 @@ static const struct hw_sequencer_funcs dcn42_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn401_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c index d2fae89dfa05..4aaecd6f8bb7 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c @@ -25,6 +25,7 @@ static const struct hw_sequencer_funcs dcn60_funcs = { .wait_for_pending_cleared = dcn10_wait_for_pending_cleared, .post_unlock_program_front_end = dcn50_post_unlock_program_front_end, .update_plane_addr = dcn20_update_plane_addr, + .prepare_plane_addr_update = dcn20_prepare_plane_addr_update, .update_dchub = dcn10_update_dchub, .update_pending_status = dcn10_update_pending_status, .program_output_csc = dcn20_program_output_csc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 4cf61eb647dd..4b2c801421d6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -92,8 +92,9 @@ struct program_triplebuffer_params { }; struct update_plane_addr_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; + struct hubp *hubp; + struct dc_plane_address address; + bool flip_immediate; }; struct set_input_transfer_func_params { @@ -1457,6 +1458,10 @@ struct hw_sequencer_funcs { struct dc_state *context); void (*update_plane_addr)(const struct dc *dc, struct pipe_ctx *pipe_ctx); + void (*prepare_plane_addr_update)(const struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_plane_address *addr_to_program, + bool *flip_immediate); void (*update_dchub)(struct dce_hwseq *hws, struct dchub_init_data *dh_data); void (*wait_for_mpcc_disconnect)(struct dc *dc, From 9f1053de9280650d73afb4c8a4de658068725dae Mon Sep 17 00:00:00 2001 From: Dillon Varone Date: Tue, 25 Aug 2026 15:50:45 -0400 Subject: [PATCH 044/114] drm/amd/display: Cleanup DMUB command submission interfaces [WHY&HOW] Allow for custom timeout passing where needed, and make inputs const where applicable. Reviewed-by: Aric Cyr Reviewed-by: Alvin Lee Signed-off-by: Dillon Varone Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 41 +++++++++++-------- drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h | 12 +++++- drivers/gpu/drm/amd/display/dmub/dmub_srv.h | 2 +- .../gpu/drm/amd/display/dmub/src/dmub_srv.c | 7 +++- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index d08dd372179c..e23137034130 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -41,8 +41,6 @@ #define DC_LOGGER CTX->logger #define GPINT_RETRY_NUM 20 -#define MAX_WAIT_US 500000 - static void dc_dmub_srv_construct(struct dc_dmub_srv *dc_srv, struct dc *dc, struct dmub_srv *dmub) { @@ -93,7 +91,7 @@ bool dc_dmub_srv_wait_for_pending(struct dc_dmub_srv *dc_dmub_srv) dmub = dc_dmub_srv->dmub; do { - status = dmub_srv_wait_for_pending(dmub, MAX_WAIT_US); + status = dmub_srv_wait_for_pending(dmub, DMUB_CMD_DEFAULT_MAX_WAIT_US); } while (dc_dmub_srv->ctx->dc->debug.disable_timeout && status != DMUB_STATUS_OK); if (status != DMUB_STATUS_OK) { @@ -123,7 +121,7 @@ void dc_dmub_srv_wait_for_inbox0_ack(struct dc_dmub_srv *dc_dmub_srv) struct dc_context *dc_ctx = dc_dmub_srv->ctx; enum dmub_status status = DMUB_STATUS_OK; - status = dmub_srv_wait_for_inbox0_ack(dmub, MAX_WAIT_US); + status = dmub_srv_wait_for_inbox0_ack(dmub, DMUB_CMD_DEFAULT_MAX_WAIT_US); if (status != DMUB_STATUS_OK) { DC_ERROR("Error waiting for INBOX0 HW Lock Ack\n"); dc_dmub_srv_handle_failure(dc_dmub_srv); @@ -146,7 +144,8 @@ void dc_dmub_srv_send_inbox0_cmd(struct dc_dmub_srv *dc_dmub_srv, static bool dc_dmub_srv_reg_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, unsigned int count, - union dmub_rb_cmd *cmd_list) + const union dmub_rb_cmd *cmd_list, + unsigned int timeout_us) { struct dc_context *dc_ctx; struct dmub_srv *dmub; @@ -162,7 +161,7 @@ static bool dc_dmub_srv_reg_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_s for (i = 0 ; i < count; i++) { /* confirm no messages pending */ do { - status = dmub_srv_wait_for_idle(dmub, MAX_WAIT_US); + status = dmub_srv_wait_for_idle(dmub, timeout_us); } while (dc_dmub_srv->ctx->dc->debug.disable_timeout && status != DMUB_STATUS_OK); /* queue command */ @@ -188,7 +187,8 @@ static bool dc_dmub_srv_reg_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_s static bool dc_dmub_srv_fb_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, unsigned int count, - union dmub_rb_cmd *cmd_list) + const union dmub_rb_cmd *cmd_list, + unsigned int timeout_us) { struct dc_context *dc_ctx; struct dmub_srv *dmub; @@ -217,7 +217,7 @@ static bool dc_dmub_srv_fb_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_sr return false; do { - status = dmub_srv_wait_for_inbox_free(dmub, MAX_WAIT_US, count - i); + status = dmub_srv_wait_for_inbox_free(dmub, timeout_us, count - i); } while (dc_dmub_srv->ctx->dc->debug.disable_timeout && status != DMUB_STATUS_OK); /* Requeue the command. */ @@ -245,17 +245,18 @@ static bool dc_dmub_srv_fb_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_sr return true; } -bool dc_dmub_srv_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, +bool dc_dmub_srv_cmd_list_queue_execute_timeout(struct dc_dmub_srv *dc_dmub_srv, unsigned int count, - union dmub_rb_cmd *cmd_list) + const union dmub_rb_cmd *cmd_list, + unsigned int timeout_us) { bool res = false; if (dc_dmub_srv && dc_dmub_srv->dmub) { if (dc_dmub_srv->dmub->inbox_type == DMUB_CMD_INTERFACE_REG) { - res = dc_dmub_srv_reg_cmd_list_queue_execute(dc_dmub_srv, count, cmd_list); + res = dc_dmub_srv_reg_cmd_list_queue_execute(dc_dmub_srv, count, cmd_list, timeout_us); } else { - res = dc_dmub_srv_fb_cmd_list_queue_execute(dc_dmub_srv, count, cmd_list); + res = dc_dmub_srv_fb_cmd_list_queue_execute(dc_dmub_srv, count, cmd_list, timeout_us); } if (res) @@ -265,9 +266,17 @@ bool dc_dmub_srv_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, return res; } +bool dc_dmub_srv_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, + unsigned int count, + const union dmub_rb_cmd *cmd_list) +{ + return dc_dmub_srv_cmd_list_queue_execute_timeout(dc_dmub_srv, count, cmd_list, DMUB_CMD_DEFAULT_MAX_WAIT_US); +} + bool dc_dmub_srv_wait_for_idle(struct dc_dmub_srv *dc_dmub_srv, enum dm_dmub_wait_type wait_type, - union dmub_rb_cmd *cmd_list) + union dmub_rb_cmd *cmd_list, + unsigned int timeout_us) { struct dmub_srv *dmub; enum dmub_status status; @@ -280,7 +289,7 @@ bool dc_dmub_srv_wait_for_idle(struct dc_dmub_srv *dc_dmub_srv, // Wait for DMUB to process command if (wait_type != DM_DMUB_WAIT_TYPE_NO_WAIT) { do { - status = dmub_srv_wait_for_idle(dmub, MAX_WAIT_US); + status = dmub_srv_wait_for_idle(dmub, timeout_us); } while (dc_dmub_srv->ctx->dc->debug.disable_timeout && status != DMUB_STATUS_OK); if (status != DMUB_STATUS_OK) { @@ -314,7 +323,7 @@ bool dc_dmub_srv_cmd_run_list(struct dc_dmub_srv *dc_dmub_srv, unsigned int coun if (!dc_dmub_srv_cmd_list_queue_execute(dc_dmub_srv, count, cmd_list)) return false; - return dc_dmub_srv_wait_for_idle(dc_dmub_srv, wait_type, cmd_list); + return dc_dmub_srv_wait_for_idle(dc_dmub_srv, wait_type, cmd_list, DMUB_CMD_DEFAULT_MAX_WAIT_US); } bool dc_dmub_srv_optimized_init_done(struct dc_dmub_srv *dc_dmub_srv) @@ -1370,7 +1379,7 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle) ips_fw->signals.bits.ips1_commit, ips_fw->signals.bits.ips2_commit); - dc_dmub_srv_wait_for_idle(dc->ctx->dmub_srv, DM_DMUB_WAIT_TYPE_WAIT, NULL); + dc_dmub_srv_wait_for_idle(dc->ctx->dmub_srv, DM_DMUB_WAIT_TYPE_WAIT, NULL, DMUB_CMD_DEFAULT_MAX_WAIT_US); memset(&new_signals, 0, sizeof(new_signals)); diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h index 5737c78853fc..8056824d90e0 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h @@ -29,6 +29,8 @@ #include "dm_services_types.h" #include "dmub/dmub_srv.h" +#define DMUB_CMD_DEFAULT_MAX_WAIT_US 500000 + struct dmub_srv; struct dc; struct dc_context; @@ -59,11 +61,17 @@ bool dc_dmub_srv_optimized_init_done(struct dc_dmub_srv *dc_dmub_srv); bool dc_dmub_srv_cmd_list_queue_execute(struct dc_dmub_srv *dc_dmub_srv, unsigned int count, - union dmub_rb_cmd *cmd_list); + const union dmub_rb_cmd *cmd_list); + +bool dc_dmub_srv_cmd_list_queue_execute_timeout(struct dc_dmub_srv *dc_dmub_srv, + unsigned int count, + const union dmub_rb_cmd *cmd_list, + unsigned int timeout_us); bool dc_dmub_srv_wait_for_idle(struct dc_dmub_srv *dc_dmub_srv, enum dm_dmub_wait_type wait_type, - union dmub_rb_cmd *cmd_list); + union dmub_rb_cmd *cmd_list, + unsigned int timeout_us); bool dc_dmub_srv_cmd_run(struct dc_dmub_srv *dc_dmub_srv, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type); diff --git a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h index d8a18cd97811..a5222ffc8ab6 100644 --- a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h @@ -1073,7 +1073,7 @@ void dmub_srv_set_power_state(struct dmub_srv *dmub, enum dmub_srv_power_state_t * DMUB_STATUS_OK - success * DMUB_STATUS_INVALID - unspecified error */ -enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, union dmub_rb_cmd *cmd); +enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, const union dmub_rb_cmd *cmd); /** diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c index 587d26c742cd..dd868e38e0b0 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c @@ -1385,7 +1385,7 @@ void dmub_srv_set_power_state(struct dmub_srv *dmub, enum dmub_srv_power_state_t dmub->power_state = dmub_srv_power_state; } -enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, union dmub_rb_cmd *cmd) +enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, const union dmub_rb_cmd *cmd) { uint64_t num_pending = 0; @@ -1411,7 +1411,10 @@ enum dmub_status dmub_srv_reg_cmd_execute(struct dmub_srv *dmub, union dmub_rb_c /* clear last rsp ack and send message */ dmub->hw_funcs.clear_reg_inbox0_rsp_int_ack(dmub); - dmub->hw_funcs.send_reg_inbox0_cmd_msg(dmub, cmd); + /* hw_funcs signature is not const as some ASIC variants may reuse the + * buffer; send_reg_inbox0_cmd_msg implementations only read from cmd. + */ + dmub->hw_funcs.send_reg_inbox0_cmd_msg(dmub, (union dmub_rb_cmd *)cmd); dmub->reg_inbox0.num_submitted++; dmub->reg_inbox0.is_pending = true; From b788a0cfb256239599069e90fafec35cefe658b4 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Wed, 2 Sep 2026 13:21:03 -0400 Subject: [PATCH 045/114] drm/amd/display: Enable power gating on dcn42b Turn on power gating by default for dcn42b in the following domains: DPP, HUBP, DSC. Root clock gating is enabled for the following domains: DPP, DSC, DPSTREAM, SYMCLK. Now DCN42b will match the exact PG and CG policies as dcn42. Reviewed-by: Matthew Stewart Signed-off-by: Gabe Teeger Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../dc/resource/dcn42b/dcn42b_resource.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 4105150a9e12..31d4f88766b2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -802,9 +802,13 @@ static const struct dc_debug_options debug_defaults_drv = { .force_abm_enable = false, .clock_trace = true, .disable_pplib_clock_request = false, - .disable_dpp_power_gate = true, - .disable_hubp_power_gate = true, + .ignore_pg = false, + .disable_dpp_power_gate = false, + .disable_hubp_power_gate = false, .disable_optc_power_gate = true, + .disable_dsc_power_gate = false, + .disable_dio_power_gate = true, + .disable_hpo_power_gate = true, .pipe_split_policy = MPC_SPLIT_AVOID, .force_single_disp_pipe_split = false, .disable_dcc = DCC_ENABLE, @@ -830,14 +834,14 @@ static const struct dc_debug_options debug_defaults_drv = { }}, .root_clock_optimization = { .bits = { - .dpp = false, - .dsc = false,/*dscclk and dsc pg*/ + .dpp = true, + .dsc = true,/*dscclk and dsc pg*/ .hdmistream = false, .hdmichar = false, - .dpstream = false, - .symclk32_se = false, - .symclk32_le = false, - .symclk_fe = false, + .dpstream = true, + .symclk32_se = true, + .symclk32_le = true, + .symclk_fe = true, .physymclk = false, .dpiasymclk = false, } @@ -866,7 +870,6 @@ static const struct dc_debug_options debug_defaults_drv = { .min_disp_clk_khz = 50000, .static_screen_wait_frames = 2, .disable_z10 = false, - .ignore_pg = true, .disable_stutter_for_wm_program = true, .min_deep_sleep_dcfclk_khz = 8000, .replay_skip_crtc_disabled = true, From ad726d863900eb0de0f03fae5b9cabd214263343 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Wed, 2 Sep 2026 13:41:54 -0400 Subject: [PATCH 046/114] drm/amd/display: Bound DSC power gating loop by num_dsc pg_cntl42_dsc_pg_status(i) should be bounded by the number of dsc, which is different from the number of DPP on dcn42b. Reviewed-by: Matthew Stewart Signed-off-by: Gabe Teeger Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c index 2c87e33e909e..79f1b79eacc2 100644 --- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c +++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c @@ -6,6 +6,7 @@ #include "core_types.h" #include "dcn42_pg_cntl.h" #include "dccg.h" +#include "resource.h" #define TO_DCN_PG_CNTL(pg_cntl)\ container_of(pg_cntl, struct dcn_pg_cntl, base) @@ -582,7 +583,10 @@ void pg_cntl42_init_pg_status(struct pg_cntl *pg_cntl) block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, i); pg_cntl->pg_pipe_res_enable[PG_HUBP][i] = block_enabled; pg_cntl->pg_pipe_res_enable[PG_DPP][i] = block_enabled; + } + /* DSC count can be lower than pipe count (e.g. DCN42B has 4 pipes, 3 DSCs) */ + for (i = 0; i < (unsigned int)pg_cntl->ctx->dc->res_pool->res_cap->num_dsc; i++) { block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, i); pg_cntl->pg_pipe_res_enable[PG_DSC][i] = block_enabled; } From 6f74688d7e2377e253671d36acd63a988186216b Mon Sep 17 00:00:00 2001 From: Dominik Kaszewski Date: Wed, 22 Jul 2026 12:28:00 +0200 Subject: [PATCH 047/114] drm/amd/display: Add lock-free memory pool [Why] Two memory limitations - limited stack size and heap allocations not allowed in interrupts - combined together lead to scratch structures and buffers scattered throughout the code, requiring piping them through interfaces. [How] * OS-provided lock-free lists still need external locking for some operations, requiring a custom implementation. * Implement memory pool which can preallocate a number of blocks with given size, then later "allocate" from them without any heap use. * Pool implemented as lock-free Treiber stack to avoid operations being blocked by suspended thread, including a same-thread interrupt exemption causing an indefinite block. * Indexed free list and generation counter protect against ABA data corruption. Reviewed-by: Nicholas Kazlauskas Signed-off-by: Dominik Kaszewski Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/Makefile | 1 + .../gpu/drm/amd/display/dc/dc_memory_pool.c | 277 ++++++++++++++++++ .../gpu/drm/amd/display/dc/dc_memory_pool.h | 107 +++++++ drivers/gpu/drm/amd/display/dc/os_types.h | 1 + 4 files changed, 386 insertions(+) create mode 100644 drivers/gpu/drm/amd/display/dc/dc_memory_pool.c create mode 100644 drivers/gpu/drm/amd/display/dc/dc_memory_pool.h diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile index a4156b2d59b9..6528bfd8e76b 100644 --- a/drivers/gpu/drm/amd/display/dc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -66,6 +66,7 @@ FILES = FILES += dc_dmub_srv.o FILES += dc_fused_io.o FILES += dc_helper.o +FILES += dc_memory_pool.o FILES += core/dc.o FILES += core/dc_debug.o FILES += core/dc_hw_sequencer.o diff --git a/drivers/gpu/drm/amd/display/dc/dc_memory_pool.c b/drivers/gpu/drm/amd/display/dc/dc_memory_pool.c new file mode 100644 index 000000000000..904f8c4a4783 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_memory_pool.c @@ -0,0 +1,277 @@ +/** + * Copyright (C) Advanced Micro Devices, Inc. All rights reserved. + * + * You may not use this software and documentation (if any) (collectively, the + * "Materials") except in compliance with the terms and conditions of the + * Software License Agreement included with the Materials or otherwise as set + * forth in writing and signed by you and an authorized signatory of AMD. + * + * If you do not have a copy of the Software License Agreement, contact your AMD + * representative for a copy. You agree that you will not reverse engineer or + * decompile the Materials, in whole or in part, except as allowed by applicable + * law. + * + * THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + */ +#include "dc_memory_pool.h" + +#include + +enum { + DC_MEMORY_POOL_SENTINEL = -1, + DC_MEMORY_POOL_ACQUIRED = -2, + DC_MEMORY_POOL_RELEASED = -3, + + // Correct autoincrement for negative numbers + DC_MEMORY_POOL_ENUM_SIZE_IMPL, + DC_MEMORY_POOL_ENUM_SIZE = 1 - DC_MEMORY_POOL_ENUM_SIZE_IMPL, +}; + +// Align everything to size of page to avoid false sharing +struct dc_memory_pool_page { + __aligned(PAGE_SIZE) char _dummy[PAGE_SIZE]; +}; + +// Generation counter protects against ABA-problem +union index_t { + struct { + int32_t value; + uint32_t generation; + } s; + + int64_t raw; +}; + +struct dc_memory_pool { + // Values and pointers constant after initialization, can share page + __aligned(PAGE_SIZE) size_t size; + size_t capacity; + + void *unaligned_pool; + void *unaligned_memory; + struct dc_memory_pool_page *memory; + atomic_t *free_list; + + // Updated every operation, use separate page to avoid false sharing + __aligned(PAGE_SIZE) atomic64_t free_head; // index_t + char _reserved2[PAGE_SIZE - sizeof(atomic64_t)]; +}; + +static_assert(sizeof(struct dc_memory_pool) == 2 * PAGE_SIZE); +static_assert(offsetof(struct dc_memory_pool, free_head) == PAGE_SIZE); + +static size_t divide_ceiling(size_t x, size_t d) +{ + return (x + d - 1) / d; +} + +static size_t round_up_to_multiple(size_t x, size_t m) +{ + return divide_ceiling(x, m) * m; +} + +static size_t size_in_pages(size_t x) +{ + return divide_ceiling(x, PAGE_SIZE); +} + +static void *page_align_up(void *p) +{ + intptr_t i = (intptr_t)p; + + i = (intptr_t)round_up_to_multiple((size_t)i, PAGE_SIZE); + return (void *)i; +} + +__must_check struct dc_memory_pool *dc_memory_pool_create(size_t size, + size_t capacity) +{ + if (!size || !capacity) + return NULL; + + // Limited by split between size and generation in index_t + if (capacity >= (uint32_t)-DC_MEMORY_POOL_ENUM_SIZE) + return NULL; + + // uint32_t because that's what alloc functions take + const uint32_t block_pages = (uint32_t)size_in_pages(size); + const uint32_t lines = (uint32_t)capacity * block_pages; + const uint32_t padded_struct_size = + sizeof(struct dc_memory_pool) + PAGE_SIZE; + + void *unaligned_pool = kzalloc(padded_struct_size, GFP_KERNEL); + + if (!unaligned_pool) + return NULL; + + struct dc_memory_pool *pool = page_align_up(unaligned_pool); + + *pool = (struct dc_memory_pool){ + .size = size, + .capacity = capacity, + .unaligned_pool = unaligned_pool, + .unaligned_memory = kcalloc(lines + 1, PAGE_SIZE, GFP_KERNEL), + .free_list = kcalloc((uint32_t)capacity, sizeof(atomic_t), + GFP_KERNEL), + .free_head = ATOMIC_INIT(0), + }; + pool->memory = page_align_up(pool->unaligned_memory); + + if (!pool->unaligned_memory || !pool->free_list) { + dc_memory_pool_destroy(pool); + return NULL; + } + + for (int32_t i = 0; i < (int32_t)capacity; i++) + atomic_set_release(&pool->free_list[i], i + 1); + + atomic_set_release(&pool->free_list[capacity - 1], + DC_MEMORY_POOL_SENTINEL); + + return pool; +} + +void dc_memory_pool_destroy(struct dc_memory_pool *pool) +{ + if (!pool) + return; + + kfree(pool->free_list); + kfree(pool->unaligned_memory); + kfree(pool->unaligned_pool); +} + +static int32_t dc_memory_pool_get_index(const struct dc_memory_pool *pool, + const void *memory) +{ + if (!pool || !memory) { + ASSERT(false); + return DC_MEMORY_POOL_SENTINEL; + } + + // Direct pointer arithmetic would be UB if called by false `owns()` + if ((uintptr_t)memory < (uintptr_t)pool->memory) + return DC_MEMORY_POOL_SENTINEL; + + const uintptr_t distance = (uintptr_t)memory - (uintptr_t)pool->memory; + const size_t block_size = size_in_pages(pool->size) * PAGE_SIZE; + const size_t i = (size_t)distance / block_size; + + if (distance % (uintptr_t)block_size != 0) + return DC_MEMORY_POOL_SENTINEL; + + if (i >= pool->capacity) + return DC_MEMORY_POOL_SENTINEL; + + return (int32_t)i; +} + +static void *dc_memory_pool_get_page(const struct dc_memory_pool *pool, + int32_t index) +{ + if (!pool) { + ASSERT(false); + return NULL; + } + + if (index < 0 || index >= (int32_t)pool->capacity) { + ASSERT(false); + return NULL; + } + + return &pool->memory[(size_t)index * size_in_pages(pool->size)]; +} + +__must_check void *dc_memory_pool_acquire(struct dc_memory_pool *pool) +{ + if (!pool) { + ASSERT(false); + return NULL; + } + + // CAS-atomic `out = head; head = head->next;` + union index_t old_head = { + .raw = atomic64_read_acquire(&pool->free_head), + }; + union index_t new_head = { + .raw = 0, + }; + int32_t i = 0; + + do { + i = old_head.s.value; + if (i == DC_MEMORY_POOL_SENTINEL) + return NULL; + + new_head = (union index_t){ + .s.value = atomic_read_acquire(&pool->free_list[i]), + .s.generation = old_head.s.generation + 1, + }; + } while (!atomic64_try_cmpxchg(&pool->free_head, &old_head.raw, + new_head.raw)); + + atomic_set_release(&pool->free_list[i], DC_MEMORY_POOL_ACQUIRED); + return dc_memory_pool_get_page(pool, i); +} + +void dc_memory_pool_release(struct dc_memory_pool *pool, void *memory) +{ + if (!dc_memory_pool_owns(pool, memory)) { + // Likely acquired in different pool or (racing?) double free + ASSERT(false); + return; + } + + const int32_t i = dc_memory_pool_get_index(pool, memory); + + if (atomic_xchg(&pool->free_list[i], DC_MEMORY_POOL_RELEASED) != + DC_MEMORY_POOL_ACQUIRED) { + // Double free from two racing threads, use krefs to sync + ASSERT(false); + return; + } + + // CAS-atomic `in->next = head; head = in;` + union index_t old_head = { + .raw = atomic64_read_acquire(&pool->free_head), + }; + union index_t new_head = { + .raw = 0, + }; + + do { + atomic_set_release(&pool->free_list[i], old_head.s.value); + new_head = (union index_t){ + .s.value = i, + .s.generation = old_head.s.generation + 1, + }; + } while (!atomic64_try_cmpxchg(&pool->free_head, &old_head.raw, + new_head.raw)); +} + +__must_check bool dc_memory_pool_owns(const struct dc_memory_pool *pool, + const void *memory) +{ + const int32_t i = dc_memory_pool_get_index(pool, memory); + + if (i < 0) + return false; + + if (atomic_read_acquire(&pool->free_list[i]) != DC_MEMORY_POOL_ACQUIRED) + return false; + + return true; +} + +__must_check size_t dc_memory_pool_size(const struct dc_memory_pool *pool) +{ + ASSERT(pool); + return pool->size; +} + +__must_check size_t dc_memory_pool_capacity(const struct dc_memory_pool *pool) +{ + ASSERT(pool); + return pool->capacity; +} diff --git a/drivers/gpu/drm/amd/display/dc/dc_memory_pool.h b/drivers/gpu/drm/amd/display/dc/dc_memory_pool.h new file mode 100644 index 000000000000..0ec305813d66 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_memory_pool.h @@ -0,0 +1,107 @@ +/** + * Copyright (C) Advanced Micro Devices, Inc. All rights reserved. + * + * You may not use this software and documentation (if any) (collectively, the + * "Materials") except in compliance with the terms and conditions of the + * Software License Agreement included with the Materials or otherwise as set + * forth in writing and signed by you and an authorized signatory of AMD. + * + * If you do not have a copy of the Software License Agreement, contact your AMD + * representative for a copy. You agree that you will not reverse engineer or + * decompile the Materials, in whole or in part, except as allowed by applicable + * law. + * + * THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + */ +#ifndef DC_MEMORY_POOL_H +#define DC_MEMORY_POOL_H + +#include "os_types.h" + +/** + * Non-resizable lock-free memory pool with fixed capacity. + * + * Custom implementation is used over Linux llist, as llist requires external + * locking if more than one thread pops from the list. + * + * Emplace and erase operations do not use any mutexes or spinlocks, which + * guarantees forward progress even if another thread + * has been suspended in the middle of the call. High contention might result + * in multiple internal retries, but will eventually either succeed once other + * threads stop actively modifying the pool, or fail if the pool is empty. + * + * Implemented as Treiber stack using indexed free list and head with generation + * counter to solve ABA problem, as each modification of the head increments + * the generation, preventing issue of `push(A)` being indistinguishable from + * `push(A); { push(B); pop(B); }` to another thread, corrupting data structure. + */ +struct dc_memory_pool; + +/** + * Create dc_memory_pool. + * + * @param size Non-zero pool block size, all acquires will be of this size. + * @param capacity Non-zero number of blocks that can be acquired from the pool. + * @return Pointer to the pool if succeeded, null if failed. + */ +__must_check struct dc_memory_pool *dc_memory_pool_create(size_t size, + size_t capacity); + +/** + * Destroy given pool and free all allocated memory, invalidating any pointers. + * + * This operation is not synchronized, calling any other operation on the pool + * while it is being destroyed results in Undefined Behavior. + * + * @param pool Can be null to support common destruction patterns. + */ +void dc_memory_pool_destroy(struct dc_memory_pool *pool); + +/** + * Acquire single block from pool. + * + * @param pool Cannot be null. + * @return Pointer to block if succeeded, null if empty. + */ +__must_check void *dc_memory_pool_acquire(struct dc_memory_pool *pool); + +/** + * Release previously acquired block, freeing it for others to acquire. + * + * @param pool Cannot be null. + * @param memory Cannot be null, has to be owned by the pool. + */ +void dc_memory_pool_release(struct dc_memory_pool *pool, void *memory); + +/** + * Check if given memory is owned by the pool to facilitate arenas. + * + * @warning if (owns(pool, p)) release(pool, p);` pattern is not safe + * if used with same `p` from multiple unsynchronized threads. + * If multiple owning threads are desired, use krefs to assure single release. + * + * @param pool Cannot be null. + * @param memory Cannot be null. + * @return True if the memory was previously acquired from the pool. + */ +__must_check bool dc_memory_pool_owns(const struct dc_memory_pool *pool, + const void *memory); + +/** + * Get pool block size. + * + * @param pool Cannot be null. + * @return Pool block size as given to dc_memory_pool_create(). + */ +__must_check size_t dc_memory_pool_size(const struct dc_memory_pool *pool); + +/** + * Get pool block count. + * + * @param pool Cannot be null. + * @return Pool block count as given to dc_memory_pool_create(). + */ +__must_check size_t dc_memory_pool_capacity(const struct dc_memory_pool *pool); + +#endif // Header guard diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h index 339372293a98..27d4a96b958f 100644 --- a/drivers/gpu/drm/amd/display/dc/os_types.h +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -27,6 +27,7 @@ #ifndef _OS_TYPES_H_ #define _OS_TYPES_H_ +#include #include #include #include From e3fe350998e2a4dc35f5af9df83baac46124e6ba Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 28 Aug 2026 10:13:33 +0800 Subject: [PATCH 048/114] drm/amd/display: Rename lock_and_validation_needed to needs_dc_state_realloc [Why & How] Split the boolean's two meanings: it only tracks whether the commit needs to rebuild the dc_state and take the global lock, not the update magnitude. Rename it so the name reflects that; no functional change. Reviewed-by: Leo Li Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) 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 b6ef2e005450..4e5718947cef 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5446,7 +5446,7 @@ STATIC_IFN_KUNIT int dm_update_crtc_state(struct amdgpu_display_manager *dm, struct drm_crtc_state *old_crtc_state, struct drm_crtc_state *new_crtc_state, bool enable, - bool *lock_and_validation_needed) + bool *needs_dc_state_realloc) { struct dm_atomic_state *dm_state = NULL; struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; @@ -5619,7 +5619,7 @@ STATIC_IFN_KUNIT int dm_update_crtc_state(struct amdgpu_display_manager *dm, amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state); - *lock_and_validation_needed = true; + *needs_dc_state_realloc = true; } else {/* Add stream for any updated/enabled CRTC */ /* @@ -5657,7 +5657,7 @@ STATIC_IFN_KUNIT int dm_update_crtc_state(struct amdgpu_display_manager *dm, goto fail; } - *lock_and_validation_needed = true; + *needs_dc_state_realloc = true; } } @@ -5889,7 +5889,7 @@ dm_update_plane_state(struct dc *dc, struct drm_plane_state *old_plane_state, struct drm_plane_state *new_plane_state, bool enable, - bool *lock_and_validation_needed, + bool *needs_dc_state_realloc, bool *is_top_most_overlay) { @@ -5960,7 +5960,7 @@ dm_update_plane_state(struct dc *dc, dm_new_plane_state->dc_state = NULL; - *lock_and_validation_needed = true; + *needs_dc_state_realloc = true; } else { /* Add new planes */ struct dc_plane_state *dc_new_plane_state; @@ -6051,7 +6051,7 @@ dm_update_plane_state(struct dc *dc, */ dm_new_plane_state->dc_state->update_bits.full_update = 1; - *lock_and_validation_needed = true; + *needs_dc_state_realloc = true; } out: @@ -6208,7 +6208,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, struct drm_plane_state *old_plane_state, *new_plane_state, *new_cursor_state; enum dc_status status; int ret, i; - bool lock_and_validation_needed = false; + bool needs_dc_state_realloc = false; bool is_top_most_overlay = true; struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; struct drm_dp_mst_topology_mgr *mgr; @@ -6382,7 +6382,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, old_plane_state, new_plane_state, false, - &lock_and_validation_needed, + &needs_dc_state_realloc, &is_top_most_overlay); if (ret) { drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret)); @@ -6396,7 +6396,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, old_crtc_state, new_crtc_state, false, - &lock_and_validation_needed); + &needs_dc_state_realloc); if (ret) { drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret)); goto fail; @@ -6409,7 +6409,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, old_crtc_state, new_crtc_state, true, - &lock_and_validation_needed); + &needs_dc_state_realloc); if (ret) { drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret)); goto fail; @@ -6422,7 +6422,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, old_plane_state, new_plane_state, true, - &lock_and_validation_needed, + &needs_dc_state_realloc, &is_top_most_overlay); if (ret) { drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret)); @@ -6537,7 +6537,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state)) continue; - lock_and_validation_needed = true; + needs_dc_state_realloc = true; } /* set the slot info for each mst_state based on the link encoding format */ @@ -6573,7 +6573,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, * * TODO: Remove this stall and drop DM state private objects. */ - if (lock_and_validation_needed) { + if (needs_dc_state_realloc) { ret = dm_atomic_get_state(state, &dm_state); if (ret) { drm_dbg_atomic(dev, "dm_atomic_get_state() failed: %pe\n", ERR_PTR(ret)); @@ -6674,7 +6674,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, * the FB pitch, the DCC state, rotation, mem_type, etc. */ if (new_crtc_state->async_flip && - (lock_and_validation_needed || + (needs_dc_state_realloc || amdgpu_dm_crtc_mem_type_changed(dev, state, new_crtc_state))) { drm_dbg_atomic(crtc->dev, "[CRTC:%d:%s] async flips are only supported for fast updates\n", @@ -6683,7 +6683,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_check(struct drm_device *dev, goto fail; } - dm_new_crtc_state->update_type = lock_and_validation_needed ? + dm_new_crtc_state->update_type = needs_dc_state_realloc ? UPDATE_TYPE_FULL : UPDATE_TYPE_FAST; } From 123f78c8fa9c16d58c19c6f74152026addfc021b Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 28 Aug 2026 10:13:33 +0800 Subject: [PATCH 049/114] drm/amd/display: Attach only plane updates that actually changed [Why & How] Cache the last programmed dc_flip_addrs/dc_plane_info/dc_scaling_info in dm_plane_state and carry them across atomic_duplicate_state. In amdgpu_dm_commit_planes() attach each flip_addr/plane_info/scaling_info surface update only when it differs from the cache (memcmp), so an address-only flip sends no plane_info/scaling_info update and stays on the FAMS2 offload path. Reviewed-by: Leo Li Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 43 ++++++++++++++++--- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 14 ++++++ .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 32 ++++++++++++++ .../amdgpu_dm/tests/amdgpu_dm_plane_test.c | 21 ++++++++- 4 files changed, 101 insertions(+), 9 deletions(-) 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 4e5718947cef..3f601a3d2d7d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3924,6 +3924,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bool plane_needs_flip; struct dc_plane_state *dc_plane; struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state); + struct dm_plane_state *dm_old_plane_state = to_dm_plane_state(old_plane_state); /* Cursor plane is handled after stream updates */ if (plane->type == DRM_PLANE_TYPE_CURSOR && @@ -3958,11 +3959,22 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bundle->surface_updates[planes_count].cm = &dc_plane->cm; } - amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, - &bundle->scaling_infos[planes_count]); + if (amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, + &bundle->scaling_infos[planes_count])) { + planes_count += 1; + continue; + } - bundle->surface_updates[planes_count].scaling_info = - &bundle->scaling_infos[planes_count]; + /* Cache the newly computed scaling_info in the plane state */ + *dm_new_plane_state->scaling_info = + bundle->scaling_infos[planes_count]; + + /* Only send a scaling_info update if it changed vs the old state */ + if (memcmp(dm_old_plane_state->scaling_info, + dm_new_plane_state->scaling_info, + sizeof(struct dc_scaling_info))) + bundle->surface_updates[planes_count].scaling_info = + &bundle->scaling_infos[planes_count]; plane_needs_flip = old_plane_state->fb && new_plane_state->fb; @@ -3985,8 +3997,16 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, new_plane_state->plane->index, bundle->plane_infos[planes_count].dcc.enable); - bundle->surface_updates[planes_count].plane_info = - &bundle->plane_infos[planes_count]; + /* Cache the newly computed plane_info in the plane state */ + *dm_new_plane_state->plane_info = + bundle->plane_infos[planes_count]; + + /* Only send a plane_info update if it changed vs the old state */ + if (memcmp(dm_old_plane_state->plane_info, + dm_new_plane_state->plane_info, + sizeof(struct dc_plane_info))) + bundle->surface_updates[planes_count].plane_info = + &bundle->plane_infos[planes_count]; if (acrtc_state->stream->link->psr_settings.psr_feature_enabled || acrtc_state->stream->link->replay_settings.replay_feature_enabled) { @@ -4041,7 +4061,16 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, timestamp_ns = ktime_get_ns(); bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000); - bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count]; + /* Cache the newly computed flip_addr in the plane state */ + *dm_new_plane_state->flip_addr = + bundle->flip_addrs[planes_count]; + + /* Only send a flip_addr update if it changed vs the old state */ + if (memcmp(dm_old_plane_state->flip_addr, + dm_new_plane_state->flip_addr, + sizeof(struct dc_flip_addrs))) + bundle->surface_updates[planes_count].flip_addr = + &bundle->flip_addrs[planes_count]; bundle->surface_updates[planes_count].surface = dc_plane; if (!bundle->surface_updates[planes_count].surface) { 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 52efebb2886d..b0ad73106a6b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -937,6 +937,10 @@ enum amdgpu_transfer_function { AMDGPU_TRANSFER_FUNCTION_COUNT }; +struct dc_flip_addrs; +struct dc_scaling_info; +struct dc_plane_info; + struct dm_plane_state { struct drm_plane_state base; struct dc_plane_state *dc_state; @@ -1006,6 +1010,16 @@ struct dm_plane_state { * applying blend LUT. */ enum amdgpu_transfer_function blend_tf; + + /* Cached per-plane surface descriptors kept in the DRM plane state. + * The DRM atomic old/new state swap lets us compare the previous + * commit's values (old) against the newly computed ones to detect a + * real plane change (vs an address-only flip) so DC only gets a + * scaling_info/plane_info surface update when it actually changed. + */ + struct dc_flip_addrs *flip_addr; + struct dc_scaling_info *scaling_info; + struct dc_plane_info *plane_info; }; enum amdgpu_dm_cursor_mode { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 4c13c7e86091..ab674a32279f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -33,6 +33,7 @@ #include #include "amdgpu.h" +#include "dc.h" #include "dal_asic_id.h" #include "amdgpu_display.h" #include "amdgpu_dm_trace.h" @@ -1806,6 +1807,18 @@ STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane) if (!amdgpu_state) return; + amdgpu_state->flip_addr = kzalloc_obj(*amdgpu_state->flip_addr); + amdgpu_state->scaling_info = kzalloc_obj(*amdgpu_state->scaling_info); + amdgpu_state->plane_info = kzalloc_obj(*amdgpu_state->plane_info); + if (!amdgpu_state->flip_addr || !amdgpu_state->scaling_info || + !amdgpu_state->plane_info) { + kfree(amdgpu_state->flip_addr); + kfree(amdgpu_state->scaling_info); + kfree(amdgpu_state->plane_info); + kfree(amdgpu_state); + return; + } + if (plane->state) plane->funcs->atomic_destroy_state(plane, plane->state); @@ -1827,6 +1840,21 @@ amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane) if (!dm_plane_state) return NULL; + dm_plane_state->flip_addr = kmemdup(old_dm_plane_state->flip_addr, + sizeof(*old_dm_plane_state->flip_addr), GFP_KERNEL); + dm_plane_state->scaling_info = kmemdup(old_dm_plane_state->scaling_info, + sizeof(*old_dm_plane_state->scaling_info), GFP_KERNEL); + dm_plane_state->plane_info = kmemdup(old_dm_plane_state->plane_info, + sizeof(*old_dm_plane_state->plane_info), GFP_KERNEL); + if (!dm_plane_state->flip_addr || !dm_plane_state->scaling_info || + !dm_plane_state->plane_info) { + kfree(dm_plane_state->flip_addr); + kfree(dm_plane_state->scaling_info); + kfree(dm_plane_state->plane_info); + kfree(dm_plane_state); + return NULL; + } + __drm_atomic_helper_plane_duplicate_state(plane, &dm_plane_state->base); if (old_dm_plane_state->dc_state) { @@ -1946,6 +1974,10 @@ STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane * if (dm_plane_state->blend_lut) drm_property_blob_put(dm_plane_state->blend_lut); + kfree(dm_plane_state->flip_addr); + kfree(dm_plane_state->scaling_info); + kfree(dm_plane_state->plane_info); + if (dm_plane_state->dc_state) dc_plane_state_release(dm_plane_state->dc_state); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index 2d2397e9135c..1001cbc334b0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -2977,7 +2977,7 @@ static void dm_test_plane_reset_initializes_state(struct kunit *test) KUNIT_EXPECT_EQ(test, new_state->shaper_tf, AMDGPU_TRANSFER_FUNCTION_DEFAULT); KUNIT_EXPECT_EQ(test, new_state->blend_tf, AMDGPU_TRANSFER_FUNCTION_DEFAULT); - kfree(new_state); + amdgpu_dm_plane_drm_plane_destroy_state(plane, &new_state->base); } /** @@ -3000,6 +3000,14 @@ static void dm_test_plane_duplicate_state_copies_fields(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, plane); KUNIT_ASSERT_NOT_NULL(test, old_state); + /* duplicate_state kmemdup()s these, so the source state must own them. */ + old_state->flip_addr = kunit_kzalloc(test, sizeof(*old_state->flip_addr), GFP_KERNEL); + old_state->scaling_info = kunit_kzalloc(test, sizeof(*old_state->scaling_info), GFP_KERNEL); + old_state->plane_info = kunit_kzalloc(test, sizeof(*old_state->plane_info), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, old_state->flip_addr); + KUNIT_ASSERT_NOT_NULL(test, old_state->scaling_info); + KUNIT_ASSERT_NOT_NULL(test, old_state->plane_info); + old_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_PQ_EOTF; old_state->hdr_mult = 0x123456789ULL; old_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_IDENTITY; @@ -3016,7 +3024,7 @@ static void dm_test_plane_duplicate_state_copies_fields(struct kunit *test) KUNIT_EXPECT_EQ(test, dup_state->blend_tf, AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF); KUNIT_EXPECT_NULL(test, dup_state->dc_state); - kfree(dup_state); + amdgpu_dm_plane_drm_plane_destroy_state(plane, dup_base); } /* @@ -3067,6 +3075,15 @@ static void dm_test_plane_duplicate_state_copies_resources(struct kunit *test) kref_init(&dc_plane_state->refcount); old_state->dc_state = dc_plane_state; dm_test_attach_color_blobs(test, &adev->ddev, old_state); + + /* duplicate_state kmemdup()s these, so the source state must own them. */ + old_state->flip_addr = kunit_kzalloc(test, sizeof(*old_state->flip_addr), GFP_KERNEL); + old_state->scaling_info = kunit_kzalloc(test, sizeof(*old_state->scaling_info), GFP_KERNEL); + old_state->plane_info = kunit_kzalloc(test, sizeof(*old_state->plane_info), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, old_state->flip_addr); + KUNIT_ASSERT_NOT_NULL(test, old_state->scaling_info); + KUNIT_ASSERT_NOT_NULL(test, old_state->plane_info); + plane->state = &old_state->base; dup_base = amdgpu_dm_plane_drm_plane_duplicate_state(plane); From 555641725f214e2a2a230338f282cdebfd9fcbf3 Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 28 Aug 2026 10:13:33 +0800 Subject: [PATCH 050/114] drm/amd/display: Request DMUB HW cursor offload [Why & How] Request cursor offload in dc_config and initialize it after DMUB is up (dc_dmub_srv_cursor_offload_init), which self-gates on the firmware cursor_offload_v1_support capability, so cursor programming no longer takes the inbox0 lock on alt-in-use streams. Reviewed-by: Leo Li Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++++++ drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) 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 3f601a3d2d7d..6f71c63b973a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -607,6 +607,13 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.flags.enable_mipi_converter_optimization = true; + /* + * Request HW cursor offload so DMUB programs the cursor; cursor + * updates then avoid the DMUB inbox0 HW lock during mclk switches. + * dc_dmub_srv_cursor_offload_init() self-gates on FW capability. + */ + init_data.flags.enable_cursor_offload = true; + init_data.dcn_reg_offsets = adev->reg_offset[DCE_HWIP][0]; init_data.nbio_reg_offsets = adev->reg_offset[NBIO_HWIP][0]; init_data.clk_reg_offsets = adev->reg_offset[CLK_HWIP][0]; @@ -724,6 +731,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) dc_hardware_init(adev->dm.dc); + /* Enable cursor offload if the DMUB firmware supports it. */ + dc_dmub_srv_cursor_offload_init(adev->dm.dc); + /* GOP/vBIOS may leave an OPTC enabled for a display present at power-on * but no longer driven (e.g. an external DP unplugged at boot). Such a * dangling pipe keeps DCN out of idle and blocks s0i3. Power it down diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index e23137034130..1e7071f8d53f 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -1211,9 +1211,16 @@ void dc_dmub_srv_subvp_save_surf_addr(const struct dc_dmub_srv *dc_dmub_srv, con void dc_dmub_srv_cursor_offload_init(struct dc *dc) { struct dmub_rb_cmd_cursor_offload_init *init; - struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv; + struct dc_dmub_srv *dc_dmub_srv; union dmub_rb_cmd cmd; + if (!dc || !dc->ctx) + return; + + dc_dmub_srv = dc->ctx->dmub_srv; + if (!dc_dmub_srv || !dc_dmub_srv->dmub) + return; + if (!dc->config.enable_cursor_offload) return; From acc92b3aad0f71959437e84b67cfaecbdcbcf315 Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 28 Aug 2026 10:14:37 +0800 Subject: [PATCH 051/114] drm/amd/display: Send stream_update to DC only when it changed [Why & How] Track stream_update_needed and pass &bundle->stream_update to DC only when a real stream property was actually written (native-cursor disable, cursor position/attributes, VRR infopacket, modeset src/dst, color management, ABM level); otherwise pass NULL. An address-only flip then carries no stream_update and stays on the FAMS2 offload path. Reviewed-by: Leo Li Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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 6f71c63b973a..e8405d235133 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3886,6 +3886,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bool flip_latched_during_prog = false; bool dirty_rects_changed = false; bool updated_planes_and_streams = false; + bool stream_update_needed = false; struct { struct dc_surface_update surface_updates[MAX_SURFACES]; struct dc_plane_info plane_infos[MAX_SURFACES]; @@ -3919,6 +3920,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bundle->stream_update.cursor_position = &acrtc_state->stream->cursor_position; + stream_update_needed = true; } if (acrtc_state->active_planes == 0 && @@ -3942,8 +3944,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, if ((fb && crtc == pcrtc) || (old_plane_state->fb && old_plane_state->crtc == pcrtc)) { cursor_update = true; - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { amdgpu_dm_update_cursor(plane, old_plane_state, &bundle->stream_update); + stream_update_needed = true; + } } continue; @@ -4152,6 +4156,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, &acrtc_state->stream->vrr_infopacket; bundle->stream_update.vsp_infopacket = &acrtc_state->stream->vsp_infopacket; + stream_update_needed = true; } } } @@ -4186,6 +4191,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, if (new_pcrtc_state->mode_changed) { bundle->stream_update.src = acrtc_state->stream->src; bundle->stream_update.dst = acrtc_state->stream->dst; + stream_update_needed = true; } if (new_pcrtc_state->color_mgmt_changed) { @@ -4203,11 +4209,14 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, (struct dc_3dlut *) acrtc_state->stream->lut3d_func; bundle->stream_update.func_shaper = (struct dc_transfer_func *) acrtc_state->stream->func_shaper; + stream_update_needed = true; } acrtc_state->stream->abm_level = acrtc_state->abm_level; - if (acrtc_state->abm_level != dm_old_crtc_state->abm_level) + if (acrtc_state->abm_level != dm_old_crtc_state->abm_level) { bundle->stream_update.abm_level = &acrtc_state->abm_level; + stream_update_needed = true; + } /* * If FreeSync state on the stream has changed then we need to @@ -4226,7 +4235,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, acrtc_state->update_type, planes_count, acrtc_state->stream, - &bundle->stream_update, + stream_update_needed ? &bundle->stream_update : NULL, bundle->surface_updates); updated_planes_and_streams = true; From acb623f3c2fb3493e8521362e07492eb46920392 Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 28 Aug 2026 10:14:37 +0800 Subject: [PATCH 052/114] drm/amd/display: Drop dead update_type param from update_planes_and_stream_adapter [Why & How] The adapter never read update_type; DC derives the update type itself inside dc_update_planes_and_stream(). Drop the dead parameter from the prototype, the two callers and the KUnit tests. Reviewed-by: Leo Li Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 ---- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 1 - drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) 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 e8405d235133..c5c728085113 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -274,7 +274,6 @@ static const struct amdgpu_dm_kunit_ops *amdgpu_dm_ops; * before passing control to DC. * * @dc: Display Core control structure - * @update_type: specify whether it is FULL/MEDIUM/FAST update * @planes_count: planes count to update * @stream: stream state * @stream_update: stream update @@ -283,7 +282,6 @@ static const struct amdgpu_dm_kunit_ops *amdgpu_dm_ops; */ STATIC_IFN_KUNIT INLINE_IFN_KUNIT bool update_planes_and_stream_adapter(struct dc *dc, - int update_type, int planes_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, @@ -1806,7 +1804,6 @@ STATIC_IFN_KUNIT void dm_gpureset_commit_state(struct dc_state *dc_state, } update_planes_and_stream_adapter(dm->dc, - UPDATE_TYPE_FULL, dc_state->stream_status[k].plane_count, dc_state->streams[k], &bundle->stream_update, @@ -4232,7 +4229,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } mutex_lock(&dm->dc_lock); update_planes_and_stream_adapter(dm->dc, - acrtc_state->update_type, planes_count, acrtc_state->stream, stream_update_needed ? &bundle->stream_update : NULL, 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 b0ad73106a6b..f198d105638b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -1251,7 +1251,6 @@ void s3_handle_mst(struct drm_device *dev, bool suspend); void dm_gpureset_commit_state(struct dc_state *dc_state, struct amdgpu_display_manager *dm); int dm_plane_layer_index_cmp(const void *a, const void *b); bool update_planes_and_stream_adapter(struct dc *dc, - int update_type, int planes_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c index 48bcd6541083..e27f6e504395 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -413,7 +413,7 @@ static void dm_test_update_planes_adapter_sorts_and_forwards(struct kunit *test) }; dm_test_install_dm_ops(test, &dm_test_plane_update_ops); - KUNIT_EXPECT_TRUE(test, update_planes_and_stream_adapter(dc, UPDATE_TYPE_FAST, 3, + KUNIT_EXPECT_TRUE(test, update_planes_and_stream_adapter(dc, 3, stream, stream_update, updates)); KUNIT_EXPECT_EQ(test, updates[0].surface->layer_index, 5); KUNIT_EXPECT_EQ(test, updates[1].surface->layer_index, 3); @@ -436,7 +436,7 @@ static void dm_test_update_planes_adapter_propagates_failure(struct kunit *test) dm_test_plane_update_ctx = (struct dm_test_plane_update_ops_ctx) { 0 }; dm_test_install_dm_ops(test, &dm_test_plane_update_ops); - KUNIT_EXPECT_FALSE(test, update_planes_and_stream_adapter(NULL, UPDATE_TYPE_FAST, 0, + KUNIT_EXPECT_FALSE(test, update_planes_and_stream_adapter(NULL, 0, NULL, NULL, NULL)); } From 2f08de495c6d443a7964e30edb0e4d16597f0cfd Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Fri, 28 Aug 2026 10:58:39 +0800 Subject: [PATCH 053/114] drm/amd/display: Flush ISM work before releasing the stream [Why] ISM timers are not tied to the atomic commit, so a timer armed before a DPMS off can still fire after the stream is released. The external display check in dcn35_apply_idle_power_optimizations() loops over the active streams, so with none left it never runs and idle is allowed on an external-only system. [How] Wait out pending and in-flight ISM work before dc_stream_release(); dc_lock is not held there, so the sync wait is safe. Rename amdgpu_dm_ism_fini() to amdgpu_dm_ism_flush() and assert dc_lock is not held. Assisted-by: Cursor:Claude-Opus-5 Reviewed-by: Leo Li Signed-off-by: Ray Wu Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 ++ .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 6 +- .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c | 21 ++++- .../drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h | 2 +- .../amdgpu_dm/tests/amdgpu_dm_ism_test.c | 83 ++++++++++--------- 5 files changed, 72 insertions(+), 47 deletions(-) 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 c5c728085113..9761fdf4ea11 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4615,6 +4615,13 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state, (!new_crtc_state->active || drm_atomic_crtc_needs_modeset(new_crtc_state))) { manage_dm_interrupts(adev, acrtc, NULL); + /* + * ISM hysteresis lives on system_dfl_wq, not the + * vblank workqueue. Wait it out so a timer armed while + * the stream existed cannot allow idle after the + * stream is released. + */ + amdgpu_dm_ism_flush(&acrtc->ism); dc_stream_release(dm_old_crtc_state->stream); } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 4b8530d734e5..a7979b418c2b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -480,9 +480,9 @@ EXPORT_IF_KUNIT(amdgpu_dm_crtc_duplicate_state); STATIC_IFN_KUNIT void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) { /* - * amdgpu_dm_ism_fini() is intentionally called in amdgpu_dm_fini(). - * It must be called before dc_destroy() in amdgpu_dm_fini() - * to avoid ISM accessing an invalid dc handle once dc is released. + * ISM workers are intentionally quiesced by amdgpu_dm_ism_disable() + * in amdgpu_dm_fini(). That must happen before dc_destroy() so ISM + * cannot access an invalid dc handle once dc is released. */ drm_crtc_cleanup(crtc); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c index 4e57572e12b6..127eaba4de60 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -647,9 +647,26 @@ void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism, EXPORT_IF_KUNIT(amdgpu_dm_ism_init); -void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism) +/** + * amdgpu_dm_ism_flush - Cancel any pending, or wait out in-flight ISM work + * + * @ism: The CRTC's idle state manager + * + * Cancels the hysteresis and SSO timers and waits for a running worker to + * finish. Callers that are about to drop the CRTC's stream use this so that a + * timer armed while the stream was still around cannot allow idle afterwards. + * + * Must not be called with dc_lock held: the workers take dc_lock themselves, + * so waiting for them under it would deadlock. + */ +void amdgpu_dm_ism_flush(struct amdgpu_dm_ism *ism) { + struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); + struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); + + lockdep_assert_not_held(&adev->dm.dc_lock); + cancel_delayed_work_sync(&ism->sso_delayed_work); cancel_delayed_work_sync(&ism->delayed_work); } -EXPORT_IF_KUNIT(amdgpu_dm_ism_fini); +EXPORT_IF_KUNIT(amdgpu_dm_ism_flush); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h index afce16f7085a..893e062bb281 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h @@ -142,7 +142,7 @@ struct amdgpu_dm_ism { void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism, struct amdgpu_dm_ism_config *config); -void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism); +void amdgpu_dm_ism_flush(struct amdgpu_dm_ism *ism); void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism, enum amdgpu_dm_ism_event event); void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c index b77df47d3095..a9c6485e2a9e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c @@ -642,31 +642,6 @@ static void dm_test_ism_init_sets_initial_state(struct kunit *test) KUNIT_EXPECT_EQ(test, ism->config.sso_num_frames, config.sso_num_frames); } -/* ===== Tests for amdgpu_dm_ism_fini ===== */ - -/** - * dm_test_ism_fini_after_init - fini cancels never-scheduled work without error - * @test: KUnit test context - */ -static void dm_test_ism_fini_after_init(struct kunit *test) -{ - struct amdgpu_dm_ism *ism = alloc_test_ism(test); - struct amdgpu_dm_ism_config config = { - .filter_num_frames = 5, - .filter_entry_count = 3, - .activation_num_delay_frames = 10, - .sso_num_frames = 2, - }; - - amdgpu_dm_ism_init(ism, &config); - /* Work was never scheduled; cancel_delayed_work_sync is a no-op. */ - amdgpu_dm_ism_fini(ism); - - /* FSM state is untouched by fini */ - KUNIT_EXPECT_EQ(test, (int)ism->current_state, - (int)DM_ISM_STATE_FULL_POWER_RUNNING); -} - /* ===== Tests for dm_ism_set_last_idle_ts ===== */ /** @@ -897,6 +872,32 @@ static void register_test_acrtc(struct amdgpu_device *adev, list_add_tail(&acrtc->base.head, &adev->ddev.mode_config.crtc_list); } +/* ===== Tests for amdgpu_dm_ism_flush ===== */ + +/** + * dm_test_ism_flush_after_init - flush cancels never-scheduled work without error + * @test: KUnit test context + */ +static void dm_test_ism_flush_after_init(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, NULL); + struct amdgpu_dm_ism *ism = &acrtc->ism; + struct amdgpu_dm_ism_config config = { + .filter_num_frames = 5, + .filter_entry_count = 3, + .activation_num_delay_frames = 10, + .sso_num_frames = 2, + }; + + amdgpu_dm_ism_init(ism, &config); + /* Work was never scheduled; cancel_delayed_work_sync is a no-op. */ + amdgpu_dm_ism_flush(ism); + + /* FSM state is untouched by flush */ + KUNIT_EXPECT_EQ(test, (int)ism->current_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + /* ===== Tests for amdgpu_dm_ism_commit_event ===== */ /** @@ -922,7 +923,7 @@ static void dm_test_ism_commit_event_no_state(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state, (int)DM_ISM_STATE_FULL_POWER_RUNNING); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -958,7 +959,7 @@ static void dm_test_ism_commit_event_cursor_transition(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state, (int)DM_ISM_STATE_FULL_POWER_RUNNING); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -988,7 +989,7 @@ static void dm_test_ism_commit_event_invalid_event(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state, (int)DM_ISM_STATE_FULL_POWER_RUNNING); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /* ===== Tests for amdgpu_dm_ism_force_full_power ===== */ @@ -1020,7 +1021,7 @@ static void dm_test_ism_force_full_power(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state, (int)DM_ISM_STATE_FULL_POWER_RUNNING); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /* ===== Tests for amdgpu_dm_ism_disable / amdgpu_dm_ism_enable ===== */ @@ -1048,7 +1049,7 @@ static void dm_test_ism_disable_enable_cycle(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state, (int)DM_ISM_STATE_FULL_POWER_RUNNING); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /* ===== Tests for dm_ism_dispatch_power_state (via commit_event) ===== */ @@ -1127,7 +1128,7 @@ static void dm_test_ism_dispatch_hysteresis_schedule_and_cancel(struct kunit *te (int)DM_ISM_STATE_HYSTERESIS_BUSY); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1183,7 +1184,7 @@ static void dm_test_ism_dispatch_optimized_idle_defers_sso(struct kunit *test) cancel_delayed_work(&acrtc->ism.sso_delayed_work); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /* @@ -1285,7 +1286,7 @@ static void dm_test_ism_commit_allows_idle_on_optimized_idle(struct kunit *test) cancel_delayed_work(&acrtc->ism.sso_delayed_work); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1322,7 +1323,7 @@ static void dm_test_ism_commit_enables_sso_on_optimized_idle_sso(struct kunit *t KUNIT_EXPECT_TRUE(test, adev->dm.dc->idle_optimizations_allowed); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1371,7 +1372,7 @@ static void dm_test_ism_commit_disallows_idle_on_timer_aborted(struct kunit *tes KUNIT_EXPECT_FALSE(test, adev->dm.dc->idle_optimizations_allowed); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1413,7 +1414,7 @@ static void dm_test_ism_exit_from_optimized_idle_disallows_idle(struct kunit *te KUNIT_EXPECT_EQ(test, acrtc->ism.next_record_idx, 1); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1452,7 +1453,7 @@ static void dm_test_ism_exit_from_sso_disallows_idle(struct kunit *test) KUNIT_EXPECT_EQ(test, acrtc->ism.next_record_idx, 1); } - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1496,7 +1497,7 @@ static void dm_test_ism_delayed_work_runs_timer_elapsed(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_ism_test_idle.calls, 1); KUNIT_EXPECT_TRUE(test, adev->dm.dc->idle_optimizations_allowed); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } /** @@ -1534,7 +1535,7 @@ static void dm_test_ism_sso_delayed_work_runs_sso_elapsed(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_ism_test_idle.calls, 3); KUNIT_EXPECT_TRUE(test, adev->dm.dc->idle_optimizations_allowed); - amdgpu_dm_ism_fini(&acrtc->ism); + amdgpu_dm_ism_flush(&acrtc->ism); } static struct kunit_case dm_ism_test_cases[] = { @@ -1582,8 +1583,8 @@ static struct kunit_case dm_ism_test_cases[] = { KUNIT_CASE(dm_test_ism_idle_delay_entry_count_exceeds_history_size), /* amdgpu_dm_ism_init */ KUNIT_CASE(dm_test_ism_init_sets_initial_state), - /* amdgpu_dm_ism_fini */ - KUNIT_CASE(dm_test_ism_fini_after_init), + /* amdgpu_dm_ism_flush */ + KUNIT_CASE(dm_test_ism_flush_after_init), /* dm_ism_set_last_idle_ts */ KUNIT_CASE(dm_test_ism_set_last_idle_ts_updates_timestamp), /* dm_ism_insert_record */ From ec0c15a270bab226ece0d1698f95a5fc00e71392 Mon Sep 17 00:00:00 2001 From: Leo Chen Date: Mon, 31 Aug 2026 16:37:48 -0400 Subject: [PATCH 054/114] drm/amd/display: Cap DML2.1 vmin ODM combine at 2:1 for eDP [Why] Currently in DML2.1, by design, the DPMs are unpinned when optimizing for Vmin PMO. This leads to an issue on DCN42 on specific panels (e.g. 4k144 eDP) where ODM4:1 is applied unexpectedly, which affects the urgent bandwidth required, raises DCFCLK to DPM1, and thus blocks Z8. [How] Clamp the ODM combine limit to 2 for eDP streams directly in pmo_dcn4_fams2_optimize_for_vmin() Reviewed-by: Wenjing Liu Signed-off-by: Leo Chen Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c index 7ae02eccd598..de078ccd91a2 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c @@ -887,6 +887,7 @@ bool pmo_dcn4_fams2_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out * const struct dml2_core_mode_support_result *mode_support_result = &in_out->base_display_config->mode_support_result; unsigned int odms_used; + int odm_combine_limit; struct dml2_stream_parameters *stream_descriptor; bool optimizable = false; @@ -901,8 +902,16 @@ bool pmo_dcn4_fams2_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out * in_out->base_display_config->stage4.unoptimizable_streams[stream_index]) return false; + /* + * Enforce a max cap on ODM combine factor of 2:1 for eDP stream + */ + odm_combine_limit = in_out->instance->odm_combine_limit; + if (display_config->stream_descriptors[stream_index].output.output_encoder == dml2_edp && + odm_combine_limit > 2) + odm_combine_limit = 2; + odms_used = mode_support_result->cfg_support_info.stream_support_info[stream_index].odms_used; - if ((int)odms_used >= in_out->instance->odm_combine_limit) + if ((int)odms_used >= odm_combine_limit) return false; memcpy(in_out->optimized_display_config, From a3bc27586dde44301f97de69a8f1025310083102 Mon Sep 17 00:00:00 2001 From: "Wang, Sung-huai" Date: Mon, 31 Aug 2026 16:25:30 +0800 Subject: [PATCH 055/114] drm/amd/display: Add is_odm_enabled callback to skip init_odm on active ODM pipes [Why] dcn10_init_pipes unconditionally calls init_odm on all enabled TGs, resetting ODM combine state even when it should be preserved. [How] Add is_odm_enabled callback to timing_generator_funcs and guard init_odm to skip when ODM is already active. Reviewed-by: Aric Cyr Signed-off-by: Danny Wang Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 4 +++- .../gpu/drm/amd/display/dc/inc/hw/timing_generator.h | 1 + drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c | 10 ++++++++++ drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h | 1 + .../gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c | 1 + drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c | 1 + drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c | 1 + 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 270b6f2d4377..7fa329229ae0 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -1750,7 +1750,9 @@ void dcn10_init_pipes(struct dc *dc, struct dc_state *context) if (tg->funcs->is_tg_enabled(tg)) { if (tg->funcs->init_odm) - tg->funcs->init_odm(tg); + if (!tg->funcs->is_odm_enabled || + !tg->funcs->is_odm_enabled(tg)) + tg->funcs->init_odm(tg); } tg->funcs->tg_init(tg); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h index 2eaba65894ab..52d4d57358cd 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h @@ -512,6 +512,7 @@ struct timing_generator_funcs { uint32_t vtotal_change_limit); void (*init_odm)(struct timing_generator *tg); + bool (*is_odm_enabled)(struct timing_generator *tg); void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg); void (*set_long_vtotal)(struct timing_generator *optc, const struct long_vtotal_params *params); void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg); diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c index 3ace83e1b50f..ce9cd32b807a 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c @@ -221,6 +221,15 @@ void optc31_set_drr( } } +bool optc3_is_odm_enabled(struct timing_generator *optc) +{ + struct optc *optc1 = DCN10TG_FROM_TG(optc); + uint32_t num_segments = 0; + + REG_GET(OPTC_DATA_SOURCE_SELECT, OPTC_NUM_OF_INPUT_SEGMENT, &num_segments); + return num_segments > 0; +} + void optc3_init_odm(struct timing_generator *optc) { struct optc *optc1 = DCN10TG_FROM_TG(optc); @@ -503,6 +512,7 @@ static const struct timing_generator_funcs dcn31_tg_funcs = { .setup_manual_trigger = optc2_setup_manual_trigger, .get_hw_timing = optc1_get_hw_timing, .init_odm = optc3_init_odm, + .is_odm_enabled = optc3_is_odm_enabled, .is_two_pixels_per_container = optc1_is_two_pixels_per_container, .read_otg_state = optc31_read_otg_state, .optc_read_reg_state = optc31_read_reg_state, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h index 119a954fe3e5..19d90c65a22b 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h @@ -274,6 +274,7 @@ bool optc31_immediate_disable_crtc(struct timing_generator *optc); void optc31_set_drr(struct timing_generator *optc, const struct drr_params *params); void optc3_init_odm(struct timing_generator *optc); +bool optc3_is_odm_enabled(struct timing_generator *optc); void optc31_read_otg_state(struct timing_generator *optc, struct dcn_otg_state *s); diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c index 7250478a5092..f5faa49680ef 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c @@ -247,6 +247,7 @@ static const struct timing_generator_funcs dcn314_tg_funcs = { .setup_manual_trigger = optc2_setup_manual_trigger, .get_hw_timing = optc1_get_hw_timing, .init_odm = optc3_init_odm, + .is_odm_enabled = optc3_is_odm_enabled, .set_odm_bypass = optc314_set_odm_bypass, .set_odm_combine = optc314_set_odm_combine, .set_h_timing_div_manual_mode = optc314_set_h_timing_div_manual_mode, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c index 897f857db448..406446e5eae5 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c @@ -614,6 +614,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs = { .setup_manual_trigger = optc2_setup_manual_trigger, .get_hw_timing = optc1_get_hw_timing, .init_odm = optc3_init_odm, + .is_odm_enabled = optc3_is_odm_enabled, .set_long_vtotal = optc35_set_long_vtotal, .is_two_pixels_per_container = optc1_is_two_pixels_per_container, .read_otg_state = optc31_read_otg_state, diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c index fd9c2b6b8b3c..1e4f2051d732 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c @@ -261,6 +261,7 @@ static struct timing_generator_funcs dcn42_tg_funcs = { .setup_manual_trigger = optc2_setup_manual_trigger, .get_hw_timing = optc1_get_hw_timing, .init_odm = optc3_init_odm, + .is_odm_enabled = optc3_is_odm_enabled, .set_long_vtotal = optc35_set_long_vtotal, .is_two_pixels_per_container = optc1_is_two_pixels_per_container, .get_optc_double_buffer_pending = optc3_get_optc_double_buffer_pending, From 094108908d5072cd7b0fcf06f90530612da8748d Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Thu, 3 Sep 2026 11:33:38 -0400 Subject: [PATCH 056/114] drm/amd/display: Program DCC as part of address update [Description] - DCC register is latched on the surface address register being updated - In this case couple the DCC programming and the address update together to ensure the DCC programming is always taken as expected - Update address programming function to take in DCC bool, use for DCN6+ and leave older DCN unchanged. Reviewed-by: Dillon Varone Signed-off-by: Alvin Lee Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/core/dc_hw_sequencer.c | 4 ++- .../amd/display/dc/hubp/dcn10/dcn10_hubp.c | 4 ++- .../amd/display/dc/hubp/dcn10/dcn10_hubp.h | 3 +- .../amd/display/dc/hubp/dcn20/dcn20_hubp.c | 5 +++- .../amd/display/dc/hubp/dcn20/dcn20_hubp.h | 3 +- .../amd/display/dc/hubp/dcn21/dcn21_hubp.c | 5 +++- .../amd/display/dc/hubp/dcn30/dcn30_hubp.c | 5 +++- .../amd/display/dc/hubp/dcn30/dcn30_hubp.h | 3 +- .../amd/display/dc/hubp/dcn401/dcn401_hubp.c | 5 +++- .../amd/display/dc/hubp/dcn401/dcn401_hubp.h | 3 +- .../amd/display/dc/hubp/dcn42/dcn42_hubp.c | 5 +++- .../amd/display/dc/hubp/dcn50/dcn50_hubp.c | 28 ++++++++++++------- .../amd/display/dc/hubp/dcn50/dcn50_hubp.h | 3 +- .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 6 ++-- .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 3 +- .../amd/display/dc/hwss/dcn201/dcn201_hwseq.c | 3 +- .../drm/amd/display/dc/hwss/hw_sequencer.h | 1 + drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h | 3 +- 18 files changed, 65 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index ccc58d42e0ab..6ed22b2e9c74 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -1713,7 +1713,8 @@ void hwss_execute_sequence(struct dc *dc, params->update_plane_addr_params.hubp->funcs->hubp_program_surface_flip_and_addr( params->update_plane_addr_params.hubp, ¶ms->update_plane_addr_params.address, - params->update_plane_addr_params.flip_immediate); + params->update_plane_addr_params.flip_immediate, + params->update_plane_addr_params.dcc); break; case DPP_SET_INPUT_TRANSFER_FUNC: hws->funcs.set_input_transfer_func(¶ms->set_input_transfer_func_params); @@ -2356,6 +2357,7 @@ void hwss_add_hubp_update_plane_addr(struct block_sequence_state *seq_state, dc->hwss.prepare_plane_addr_update(dc, pipe_ctx, ¶ms->address, ¶ms->flip_immediate); params->hubp = hubp; + params->dcc = pipe_ctx->plane_state ? pipe_ctx->plane_state->dcc.enable : false; seq_state->steps[*seq_state->num_steps].func = HUBP_UPDATE_PLANE_ADDR; (*seq_state->num_steps)++; } diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c index d8eb5996b577..2a4be0c7a724 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c @@ -350,10 +350,12 @@ void hubp1_program_pixel_format( bool hubp1_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); + (void)dcc; //program flip type REG_UPDATE(DCSURF_FLIP_CONTROL, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h index f2571076fc50..0c1d5e518f97 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h @@ -905,7 +905,8 @@ void hubp_reset(struct hubp *hubp); bool hubp1_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); bool hubp1_is_flip_pending(struct hubp *hubp); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c index cf2c9f80056e..79a429814061 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c @@ -730,10 +730,13 @@ bool hubp2_dmdata_status_done(struct hubp *hubp) bool hubp2_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + (void)dcc; + //program flip type REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, flip_immediate); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h index ea90dee05d5f..3ae06bf89f79 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h @@ -399,7 +399,8 @@ void hubp2_program_deadline( bool hubp2_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); void hubp2_dcc_control(struct hubp *hubp, bool enable, enum hubp_ind_block_size independent_64b_blks); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c index 0dba8ff8a19d..c667db319baf 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c @@ -696,10 +696,13 @@ static void dmcub_PLAT_54186_wa(struct hubp *hubp, static bool hubp21_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct surface_flip_registers flip_regs = { 0 }; + (void)dcc; + flip_regs.vmid = address->vmid; switch (address->type) { diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c index 4f5f10f6d1cc..72698b5cf5f7 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c @@ -68,10 +68,13 @@ void hubp3_set_vm_system_aperture_settings(struct hubp *hubp, bool hubp3_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + (void)dcc; + //program flip type REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, flip_immediate); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h index c767e9f4f9b3..faa7e0379f4e 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h @@ -260,7 +260,8 @@ void hubp3_set_vm_system_aperture_settings(struct hubp *hubp, bool hubp3_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); void hubp3_program_surface_config( struct hubp *hubp, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index 2acdac35a382..5ca40941c355 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -486,10 +486,13 @@ void hubp401_setup_interdependent( bool hubp401_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + (void)dcc; + //program flip type REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, flip_immediate); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h index adf343837652..818cea479272 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h @@ -271,7 +271,8 @@ void hubp401_setup_interdependent( bool hubp401_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); void hubp401_dcc_control(struct hubp *hubp, struct dc_plane_dcc_param *dcc); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c index 96d3310e2c3f..0c8a23cef1e0 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c @@ -398,10 +398,13 @@ void hubp42_program_3dlut_fl_config(struct hubp *hubp, static bool hubp42_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + (void)dcc; + //program flip type REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, flip_immediate); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c index 0230f03590cf..ba86e30b1180 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c @@ -22,7 +22,8 @@ bool hubp50_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate) + bool flip_immediate, + bool dcc) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); @@ -55,8 +56,10 @@ bool hubp50_program_surface_flip_and_addr( if (address->grph.addr.quad_part == 0) break; - REG_UPDATE(DCSURF_SURFACE_CONTROL, - PRIMARY_SURFACE_TMZ, address->tmz_surface); + REG_UPDATE_3(DCSURF_SURFACE_CONTROL, + PRIMARY_SURFACE_TMZ, address->tmz_surface, + PRIMARY_SURFACE_DCC_EN, dcc, + SECONDARY_SURFACE_DCC_EN, dcc); REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, 0, PRIMARY_SURFACE_ADDRESS_HIGH, @@ -71,9 +74,11 @@ bool hubp50_program_surface_flip_and_addr( || address->video_progressive.chroma_addr.quad_part == 0) break; - REG_UPDATE_2(DCSURF_SURFACE_CONTROL, + REG_UPDATE_4(DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ, address->tmz_surface, - PRIMARY_SURFACE_TMZ_C, address->tmz_surface); + PRIMARY_SURFACE_TMZ_C, address->tmz_surface, + PRIMARY_SURFACE_DCC_EN, dcc, + SECONDARY_SURFACE_DCC_EN, dcc); REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, PRIMARY_SURFACE_ADDRESS_HIGH_C, @@ -96,11 +101,13 @@ bool hubp50_program_surface_flip_and_addr( || address->grph_stereo.right_addr.quad_part == 0) break; - REG_UPDATE_4(DCSURF_SURFACE_CONTROL, + REG_UPDATE_6(DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ, address->tmz_surface, PRIMARY_SURFACE_TMZ_C, address->tmz_surface, SECONDARY_SURFACE_TMZ, address->tmz_surface, - SECONDARY_SURFACE_TMZ_C, address->tmz_surface); + SECONDARY_SURFACE_TMZ_C, address->tmz_surface, + PRIMARY_SURFACE_DCC_EN, dcc, + SECONDARY_SURFACE_DCC_EN, dcc); REG_SET(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, 0, SECONDARY_SURFACE_ADDRESS_HIGH_C, @@ -139,9 +146,11 @@ bool hubp50_program_surface_flip_and_addr( || address->rgbea.alpha_addr.quad_part == 0) break; - REG_UPDATE_2(DCSURF_SURFACE_CONTROL, + REG_UPDATE_4(DCSURF_SURFACE_CONTROL, PRIMARY_SURFACE_TMZ, address->tmz_surface, - PRIMARY_SURFACE_TMZ_C, address->tmz_surface); + PRIMARY_SURFACE_TMZ_C, address->tmz_surface, + PRIMARY_SURFACE_DCC_EN, dcc, + SECONDARY_SURFACE_DCC_EN, dcc); REG_SET(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, 0, PRIMARY_SURFACE_ADDRESS_HIGH_C, @@ -457,7 +466,6 @@ void hubp50_program_surface_config( (void)compat_level; struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); - hubp401_dcc_control(hubp, dcc); hubp50_program_tiling(hubp2, tiling_info, format); hubp401_program_size(hubp, format, plane_size, dcc); hubp2_program_rotation(hubp, rotation, horizontal_mirror); diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h index e7d9bb3623b9..a497a90947b6 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.h @@ -21,7 +21,8 @@ bool hubp50_program_surface_flip_and_addr( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); void hubp50_program_surface_config( struct hubp *hubp, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 7fa329229ae0..84e146f2509c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -2100,7 +2100,8 @@ void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( pipe_ctx->plane_res.hubp, &address, - flip_immediate); + flip_immediate, + pipe_ctx->plane_state->dcc.enable); } bool dcn10_set_input_transfer_func(struct set_input_transfer_func_params *params) @@ -4184,5 +4185,6 @@ void dcn10_reset_surface_dcc_and_tiling(struct pipe_ctx *pipe_ctx, /* force page flip to see the new content of the framebuffer */ hubp->funcs->hubp_program_surface_flip_and_addr(hubp, &plane_state->address, - true); + true, + plane_state->dcc.enable); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index a3d2385bc731..e5f266b0013d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -2818,7 +2818,8 @@ void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( pipe_ctx->plane_res.hubp, &address, - flip_immediate); + flip_immediate, + pipe_ctx->plane_state->dcc.enable); } void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c index 916b6804e8e6..c22587188c98 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c @@ -176,7 +176,8 @@ void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( pipe_ctx->plane_res.hubp, &address, - flip_immediate); + flip_immediate, + pipe_ctx->plane_state->dcc.enable); } /* Blank pixel data during initialization */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 4b2c801421d6..f023bef4fd7e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -95,6 +95,7 @@ struct update_plane_addr_params { struct hubp *hubp; struct dc_plane_address address; bool flip_immediate; + bool dcc; }; struct set_input_transfer_func_params { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index b97f08b20984..a648ed3b012a 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -176,7 +176,8 @@ struct hubp_funcs { bool (*hubp_program_surface_flip_and_addr)( struct hubp *hubp, const struct dc_plane_address *address, - bool flip_immediate); + bool flip_immediate, + bool dcc); void (*hubp_program_pte_vm)( struct hubp *hubp, From 5d26de23eff92604735641ca8ca6f599e7e5c6c6 Mon Sep 17 00:00:00 2001 From: Tony Cheng Date: Wed, 2 Sep 2026 22:10:15 -0400 Subject: [PATCH 057/114] drm/amd/display: Add instance field to struct dccg [Why] struct dccg has no instance identifier, unlike other DC hardware blocks. Debug and tracing helpers need one to identify the block. [How] Add an inst member to struct dccg and initialize it to 0 in every dccgX_create(), since there is one DCCG instance per ASIC. Reviewed-by: Jun Lei Signed-off-by: Tony Cheng Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c | 2 ++ drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c | 1 + drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h | 1 + 13 files changed, 14 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c index 13ba7f5ce13e..71ebdd54f8f0 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c @@ -212,6 +212,7 @@ struct dccg *dccg2_create( base->ctx = ctx; base->funcs = &dccg2_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c index 5b9ba9a811ea..ee27d7519d2d 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c @@ -82,6 +82,7 @@ struct dccg *dccg201_create( base->ctx = ctx; base->funcs = &dccg201_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c index 1f23dfccf07a..d0735022f198 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c @@ -158,6 +158,7 @@ struct dccg *dccg21_create( base->ctx = ctx; base->funcs = &dccg21_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c index 9df06df6c7f1..005ca01d7ecc 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c @@ -114,6 +114,7 @@ struct dccg *dccg3_create( base->ctx = ctx; base->funcs = &dccg3_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; @@ -139,6 +140,7 @@ struct dccg *dccg30_create( base->ctx = ctx; base->funcs = &dccg3_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c index 837f4e3d1a60..022e8d3dfce2 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c @@ -73,6 +73,7 @@ struct dccg *dccg301_create( base->ctx = ctx; base->funcs = &dccg301_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c index ce90cba853f7..341509ad6f35 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c @@ -965,6 +965,7 @@ struct dccg *dccg31_create( base->ctx = ctx; base->funcs = &dccg31_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c index 1a4d4cd58ad8..a71851779b37 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c @@ -429,6 +429,7 @@ struct dccg *dccg314_create( base->ctx = ctx; base->funcs = &dccg314_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c index f75c5aa2d23e..52161c745a48 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c @@ -441,6 +441,7 @@ struct dccg *dccg32_create( base->ctx = ctx; base->funcs = &dccg32_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c index 4fbd40b518cd..ba624f442915 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c @@ -2804,6 +2804,7 @@ struct dccg *dccg35_create( base->ctx = ctx; base->funcs = &dccg35_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c index 7bff1b9da6f9..a4ece209d600 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c @@ -999,6 +999,7 @@ struct dccg *dccg401_create( base->ctx = ctx; base->funcs = &dccg401_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c index 2f498daf8f2b..bdf2bd388be7 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c @@ -382,6 +382,7 @@ struct dccg *dccg42_create( base->ctx = ctx; base->funcs = &dccg42_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c index 1d6d193b23c7..e08a9679aaf7 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c @@ -179,6 +179,7 @@ struct dccg *dccg60_create( base->ctx = ctx; base->funcs = &dccg60_funcs; + base->inst = 0; dccg_dcn->regs = regs; dccg_dcn->dccg_shift = dccg_shift; dccg_dcn->dccg_mask = dccg_mask; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h index e756719308ab..4b7c55eeb0c6 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h @@ -193,6 +193,7 @@ struct dcn_dccg_reg_state { struct dccg { struct dc_context *ctx; const struct dccg_funcs *funcs; + int inst; int pipe_dppclk_khz[MAX_PIPES]; int ref_dppclk; bool dpp_clock_gated[MAX_PIPES]; From bbd277136c67f87fcb6f78c21996197fe1dda9ca Mon Sep 17 00:00:00 2001 From: Tony Cheng Date: Wed, 2 Sep 2026 15:03:41 -0400 Subject: [PATCH 058/114] drm/amd/display: Add SPDX license identifier to dcn30_dpp_cm.c [Why&How] dcn30_dpp_cm.c carries the full MIT license text but no SPDX tag. Add the SPDX-License-Identifier line so the file matches the rest of the tree. Reviewed-by: Jun Lei Signed-off-by: Tony Cheng Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c index 8fe3474411b7..083ab6acb687 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c @@ -1,6 +1,8 @@ /* * Copyright 2020 Advanced Micro Devices, Inc. * + * SPDX-License-Identifier: MIT + * * 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 From 60484b2f024b6a992da050fad1ee39af304fd4d2 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Thu, 3 Sep 2026 11:53:38 -0400 Subject: [PATCH 059/114] drm/amd/display: Remove MALL capabilities from DCN42B [Why] DCN42B has no MALL, but the resource file advertised MALL and CAB sizes. SubVP was also enabled even though its phantom surfaces are allocated in MALL. [How] Drop the MALL and SubVP caps, the dml2_options svp_pstate and mall_cfg setup, and the add_phantom_pipes and calculate_mall_ways_from_bytes callbacks. Disable SubVP. Reviewed-by: Matthew Stewart Signed-off-by: Gabe Teeger Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../dc/resource/dcn42b/dcn42b_resource.c | 45 +------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 31d4f88766b2..de3bd23f4736 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -851,7 +851,8 @@ static const struct dc_debug_options debug_defaults_drv = { .minimum_z8_residency_time = 1, /* Always allow when other conditions are met */ .support_eDP1_5 = true, .use_max_lb = true, - .force_disable_subvp = false, + /* SubVP phantom surfaces require MALL, which DCN42B does not have */ + .force_disable_subvp = true, .exit_idle_opt_for_cursor_updates = true, .using_dml2 = true, .using_dml21 = true, @@ -1970,8 +1971,6 @@ static struct resource_funcs dcn42b_res_pool_funcs = { .get_panel_config_defaults = dcn42b_get_panel_config_defaults, //.get_preferred_eng_id_dpia = dcn42b_get_preferred_eng_id_dpia, .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, - .add_phantom_pipes = dcn32_add_phantom_pipes, - .calculate_mall_ways_from_bytes = dcn32_calculate_mall_ways_from_bytes, .prepare_mcache_programming = dcn42b_prepare_mcache_programming, .build_pipe_pix_clk_params = dcn42b_build_pipe_pix_clk_params, .get_power_profile = dcn401_get_power_profile, @@ -2037,27 +2036,7 @@ static bool dcn42b_resource_construct( dc->caps.cursor_not_scaled = true; dc->caps.min_horizontal_blanking_period = 80; dc->caps.dmdata_alloc_size = 2048; - dc->caps.mall_size_per_mem_channel = 4; - /* total size = mall per channel * num channels * 1024 * 1024 */ - dc->caps.mall_size_total = dc->caps.mall_size_per_mem_channel * - dc->ctx->dc_bios->vram_info.num_chans * 1048576; dc->caps.cursor_cache_size = dc->caps.max_cursor_size * dc->caps.max_cursor_size * 8; - dc->caps.cache_line_size = 64; - dc->caps.cache_num_ways = 16; - - /* Calculate the available MALL space */ - dc->caps.max_cab_allocation_bytes = - dcn32_calc_num_avail_chans_for_mall(dc, dc->ctx->dc_bios->vram_info.num_chans) * - dc->caps.mall_size_per_mem_channel * 1024 * 1024; - dc->caps.mall_size_total = dc->caps.max_cab_allocation_bytes; - - dc->caps.subvp_fw_processing_delay_us = 15; - dc->caps.subvp_drr_max_vblank_margin_us = 40; - dc->caps.subvp_prefetch_end_to_mall_start_us = 15; - dc->caps.subvp_swath_height_margin_lines = 16; - dc->caps.subvp_pstate_allow_width_us = 20; - dc->caps.subvp_vertical_int_margin_us = 30; - dc->caps.subvp_drr_vblank_start_margin_us = 100; // 100us margin dc->caps.max_slave_planes = 2; dc->caps.max_slave_yuv_planes = 2; @@ -2445,26 +2424,6 @@ static bool dcn42b_resource_construct( resource_init_common_dml2_callbacks(dc, &dc->dml2_options); dc->dml2_options.callbacks.can_support_mclk_switch_using_fw_based_vblank_stretch = &dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch; - dc->dml2_options.svp_pstate.callbacks.release_dsc = &dcn20_release_dsc; - dc->dml2_options.svp_pstate.callbacks.calculate_mall_ways_from_bytes = - pool->base.funcs->calculate_mall_ways_from_bytes; - - dc->dml2_options.svp_pstate.subvp_fw_processing_delay_us = dc->caps.subvp_fw_processing_delay_us; - dc->dml2_options.svp_pstate.subvp_prefetch_end_to_mall_start_us = dc->caps.subvp_prefetch_end_to_mall_start_us; - dc->dml2_options.svp_pstate.subvp_pstate_allow_width_us = dc->caps.subvp_pstate_allow_width_us; - dc->dml2_options.svp_pstate.subvp_swath_height_margin_lines = dc->caps.subvp_swath_height_margin_lines; - - dc->dml2_options.svp_pstate.force_disable_subvp = dc->debug.force_disable_subvp; - dc->dml2_options.svp_pstate.force_enable_subvp = dc->debug.force_subvp_mclk_switch; - - dc->dml2_options.mall_cfg.cache_line_size_bytes = dc->caps.cache_line_size; - dc->dml2_options.mall_cfg.cache_num_ways = dc->caps.cache_num_ways; - dc->dml2_options.mall_cfg.max_cab_allocation_bytes = - dc->caps.max_cab_allocation_bytes; - dc->dml2_options.mall_cfg.mblk_height_4bpe_pixels = DCN3_2_MBLK_HEIGHT_4BPE; - dc->dml2_options.mall_cfg.mblk_height_8bpe_pixels = DCN3_2_MBLK_HEIGHT_8BPE; - dc->dml2_options.mall_cfg.mblk_size_bytes = DCN3_2_MALL_MBLK_SIZE_BYTES; - dc->dml2_options.mall_cfg.mblk_width_pixels = DCN3_2_MBLK_WIDTH; dc->dml2_options.max_segments_per_hubp = 24; dc->dml2_options.det_segment_size = DCN42_CRB_SEGMENT_SIZE_KB; From 3366975818ebff2890d6f712c76acea3d0a81f3b Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Thu, 3 Sep 2026 11:58:45 -0400 Subject: [PATCH 060/114] drm/amd/display: Remove MALL capabilities from DCN42B bounding box [Why] DCN42B has no MALL, but MALL capabilities are non-zero. [How] Zero out MALL capabilities in the dcn42b bounding box. Reviewed-by: Matthew Stewart Reviewed-by: Dillon Varone Signed-off-by: Gabe Teeger Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h index 0f8666788bbb..e83a9ac018db 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h @@ -202,7 +202,7 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = { .xtalclk_mhz = 24, .pcie_refclk_mhz = 100, .dchub_refclk_mhz = 50, - .mall_allocated_for_dcn_mbytes = 64, + .mall_allocated_for_dcn_mbytes = 0, .max_outstanding_reqs = 256, .fabric_datapath_to_dcn_data_return_bytes = 32, .return_bus_width_bytes = 64, From 70de0a0216583a53c946155f8c8adedfdca6b4e7 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 25 Aug 2026 23:21:53 -0400 Subject: [PATCH 061/114] drm/amd/display: Atomize IRQ register read/modify/write ops [Why] The OTG_GLOBAL_SYNC_STATUS register controls various HW IRQ sources for the output timing generator (OTG). VUPDATE_NO_LOCK is one of them. To enable the IRQ, driver sets the VUPDATE_NO_LOCK_EN bit in the GLOBAL_SYNC_STATUS register. To ack the IRQ after it fires, the driver sets the VUPDATE_NO_LOCK_CLEAR bit in the same GLOBAL_SYNC_STATUS register. The bit sets are done through read/modify/write operations, which are not atomic. Thus, the following race is possible: Thread A: IRQ handler: *HW IRQ fires* # IRQ disable val = read(GLOBAL_SYNC_STATUS) unset(val, VUPDATE_NO_LOCK_EN) write(val, GLOBAL_SYNC_STATUS) # ACK reads VUPDATE_NO_LOCK_EN unset val1 = read(GLOBAL_SYNC_STATUS) set(val1, VUPDATE_NO_LOCK_CLEAR) # IRQ enable val = read(GLOBAL_SYNC_STATUS) set(val, VUPDATE_NO_LOCK_EN) write(val, GLOBAL_SYNC_STATUS) # BAD! clears VUPDATE_NO_LOCK_EN write(val1, GLOBAL_SYNC_STATUS) Regarding the tagged Fixes: change, it appears the change made this race more likely to occur. Since VUPDATE_NO_LOCK is now the sole IRQ source for vblank handling, a single race on high refresh panels can lead to a time out. [How] The GLOBAL_SYNC_STATUS register is only one example, other IRQ control registers also share the same scheme. On top of GLOBAL_SYNC_STATUS, let's clean up those as well. To keep things simple, Let's atomize the IRQ rmw ops via a single driver-wide spinlock. Due to the small scope of this lock, it is unlikely to cause noticeable overhead on top of all the existing locking within the IRQ set/handle paths. Since DM is responsible for locking, wrap dc_interrupt_set/ack with the spinlock in the new amdgpu_dm_irq_set/ack functions. Migrate/drop all references in DM to dc_interrupt_set/ack to use amdgpu_dm_irq_set/ack instead. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5616 Fixes: c87e6635d2db ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Reviewed-by: Mario Limonciello Signed-off-by: Leo Li Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 +- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 12 ++++ .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 3 +- .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 3 +- .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 64 +++++++++++-------- .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h | 28 ++++++++ .../amdgpu_dm/tests/amdgpu_dm_crtc_test.c | 16 +++-- .../amdgpu_dm/tests/amdgpu_dm_helpers_test.c | 8 ++- .../amdgpu_dm/tests/amdgpu_dm_irq_test.c | 34 +++++----- 9 files changed, 117 insertions(+), 55 deletions(-) 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 9761fdf4ea11..b5fef1f0b86b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1471,7 +1471,7 @@ STATIC_IFN_KUNIT void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, if (acrtc && state->stream_status[i].plane_count != 0 && amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; - rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; + rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; if (rc) drm_warn(adev_to_drm(adev), "Failed to %s pflip interrupts\n", enable ? "enable" : "disable"); @@ -1495,7 +1495,7 @@ STATIC_IFN_KUNIT void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, /* During gpu-reset we disable and then enable vblank irq, so * don't use amdgpu_irq_get/put() to avoid refcount change. */ - if (!dc_interrupt_set(adev->dm.dc, irq_source, enable)) + if (!amdgpu_dm_irq_set(adev, irq_source, enable)) drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis"); } else if (acrtc && state->stream_status[i].plane_count != 0) { 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 f198d105638b..a902a253be45 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -555,6 +555,18 @@ struct amdgpu_display_manager { struct common_irq_params vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1]; + /** + * @irq_reg_lock: + * + * Serializes the read-modify-writes of the HW interrupt control + * registers. Several interrupt sources share one register - e.g. the + * enable and clear bits of both VSTARTUP (vblank) and VUPDATE_NO_LOCK + * live in OTG_GLOBAL_SYNC_STATUS. Therefore, enabling one source must + * not race with acking another. Held only across amdgpu_dm_irq_set() + * and amdgpu_dm_irq_ack(). + */ + spinlock_t irq_reg_lock; + /** * @dmub_trace_params: * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index a7979b418c2b..fa6a481c8765 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -31,6 +31,7 @@ #include "amdgpu_dm_psr.h" #include "amdgpu_dm_replay.h" #include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_irq.h" #include "amdgpu_dm_plane.h" #include "amdgpu_dm_trace.h" #include "amdgpu_dm_debugfs.h" @@ -91,7 +92,7 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst; - rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; + rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", acrtc->crtc_id, enable ? "en" : "dis", rc); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 5d4f578851ae..3217aa82bea2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -1470,12 +1470,13 @@ EXPORT_IF_KUNIT(dm_helpers_free_gpu_mem); bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enable) { + struct amdgpu_device *adev = ctx->driver_context; enum dc_irq_source irq_source; bool ret; irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; - ret = dc_interrupt_set(ctx->dc, irq_source, enable); + ret = amdgpu_dm_irq_set(adev, irq_source, enable); DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n", enable ? "en" : "dis", ret); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index f997a50c1293..c3ed63843202 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -396,6 +396,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) DRM_DEBUG_KMS("DM_IRQ\n"); spin_lock_init(&adev->dm.irq_handler_list_table_lock); + spin_lock_init(&adev->dm.irq_reg_lock); adev->dm.irq_wq = alloc_workqueue("amdgpu_dm_irq", WQ_UNBOUND | WQ_HIGHPRI, 0); @@ -530,7 +531,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev) */ for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6RX; src++) { hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; - dc_interrupt_set(adev->dm.dc, src, false); + amdgpu_dm_irq_set(adev, src, false); DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); @@ -568,7 +569,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev) hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) - dc_interrupt_set(adev->dm.dc, src, true); + amdgpu_dm_irq_set(adev, src, true); } DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); @@ -594,7 +595,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) - dc_interrupt_set(adev->dm.dc, src, true); + amdgpu_dm_irq_set(adev, src, true); } DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); @@ -693,6 +694,23 @@ STATIC_IFN_KUNIT void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev, } EXPORT_IF_KUNIT(amdgpu_dm_irq_immediate_work); +bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, + bool enable) +{ + guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); + + return dc_interrupt_set(adev->dm.dc, src, enable); +} +EXPORT_IF_KUNIT(amdgpu_dm_irq_set); + +void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src) +{ + guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); + + dc_interrupt_ack(adev->dm.dc, src); +} +EXPORT_IF_KUNIT(amdgpu_dm_irq_ack); + /** * amdgpu_dm_irq_handler - Generic DM IRQ handler * @adev: amdgpu base driver device containing the DM device @@ -718,7 +736,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_irq_handler(struct amdgpu_device *adev, return 0; } - dc_interrupt_ack(adev->dm.dc, src); + amdgpu_dm_irq_ack(adev, src); /* Call high irq work immediately */ amdgpu_dm_irq_immediate_work(adev, src); @@ -758,7 +776,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev, enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type); bool st = (state == AMDGPU_IRQ_STATE_ENABLE); - dc_interrupt_set(adev->dm.dc, src, st); + amdgpu_dm_irq_set(adev, src, st); return 0; } EXPORT_IF_KUNIT(amdgpu_dm_set_hpd_irq_state); @@ -793,7 +811,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev, if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed) dc_allow_idle_optimizations(dc, false); - dc_interrupt_set(adev->dm.dc, irq_source, st); + amdgpu_dm_irq_set(adev, irq_source, st); return 0; } @@ -850,7 +868,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *a enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; bool st = (state == AMDGPU_IRQ_STATE_ENABLE); - dc_interrupt_set(adev->dm.dc, irq_source, st); + amdgpu_dm_irq_set(adev, irq_source, st); return 0; } EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_outbox_irq_state); @@ -878,7 +896,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *ad enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0; bool st = (state == AMDGPU_IRQ_STATE_ENABLE); - dc_interrupt_set(adev->dm.dc, irq_source, st); + amdgpu_dm_irq_set(adev, irq_source, st); return 0; } EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_trace_irq_state); @@ -945,9 +963,7 @@ EXPORT_IF_KUNIT(amdgpu_dm_set_irq_funcs); void amdgpu_dm_outbox_init(struct amdgpu_device *adev) { - dc_interrupt_set(adev->dm.dc, - DC_IRQ_SOURCE_DMCUB_OUTBOX, - true); + amdgpu_dm_irq_set(adev, DC_IRQ_SOURCE_DMCUB_OUTBOX, true); } EXPORT_IF_KUNIT(amdgpu_dm_outbox_init); @@ -970,7 +986,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) /* First, clear all hpd and hpdrx interrupts */ for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) { - if (!dc_interrupt_set(adev->dm.dc, i, false)) + if (!amdgpu_dm_irq_set(adev, i, false)) drm_err(dev, "Failed to clear hpd(rx) source=%d on init\n", i); } @@ -999,7 +1015,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) * of dm. Note that only hpd interrupt types are registered with * base driver; hpd_rx types aren't. IOW, amdgpu_irq_get/put on * hpd_rx isn't available. DM currently controls hpd_rx - * explicitly with dc_interrupt_set() + * explicitly with amdgpu_dm_irq_set() */ if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) { irq_type = dc_link->irq_source_hpd - DC_IRQ_SOURCE_HPD1; @@ -1008,23 +1024,21 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) * and what bios reports as the # of connectors with hpd * sources. Since the # of hpd source types registered * with base driver == mode_info.num_hpd, we have to - * fallback to dc_interrupt_set for the remaining types. + * fallback to amdgpu_dm_irq_set for the remaining types. */ if (irq_type < adev->mode_info.num_hpd) { if (amdgpu_irq_get(adev, &adev->hpd_irq, irq_type)) drm_err(dev, "DM_IRQ: Failed get HPD for source=%d)!\n", dc_link->irq_source_hpd); } else { - dc_interrupt_set(adev->dm.dc, - dc_link->irq_source_hpd, - true); + amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, + true); } } if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { - dc_interrupt_set(adev->dm.dc, - dc_link->irq_source_hpd_rx, - true); + amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, + true); } } drm_connector_list_iter_end(&iter); @@ -1069,16 +1083,14 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev) drm_err(dev, "DM_IRQ: Failed put HPD for source=%d!\n", dc_link->irq_source_hpd); } else { - dc_interrupt_set(adev->dm.dc, - dc_link->irq_source_hpd, - false); + amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, + false); } } if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { - dc_interrupt_set(adev->dm.dc, - dc_link->irq_source_hpd_rx, - false); + amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, + false); } } drm_connector_list_iter_end(&iter); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h index 4c200a9614a7..bc16ecc67329 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h @@ -89,6 +89,34 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, enum dc_irq_source irq_source, void *ih_index); +/** + * amdgpu_dm_irq_set - enable or disable a DC interrupt source. + * + * @adev: AMD DRM device + * @src: DC interrupt source to toggle + * @enable: true to enable the source, false to disable it + * + * DM-wide replacement for dc_interrupt_set(). As locking is DM's + * responsibility, this is a thin wrapper serializes the underlying + * read-modify-write against the other interrupt sources sharing HW control + * registers with @src, so DM must never call dc_interrupt_set() directly. + * + * Returns: true if the source was toggled. + */ +bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, + bool enable); + +/** + * amdgpu_dm_irq_ack - acknowledge a DC interrupt source. + * + * @adev: AMD DRM device + * @src: DC interrupt source to acknowledge + * + * DM-wide replacement for dc_interrupt_ack(), serialized the same way as + * amdgpu_dm_irq_set(). + */ +void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src); + void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev); void amdgpu_dm_outbox_init(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c index bc2b115b48b0..2e6301a92c5c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c @@ -439,7 +439,7 @@ static void dm_test_crtc_set_vupdate_irq_no_otg(struct kunit *test) * dm_test_crtc_set_vupdate_irq_dc_busy - Test vupdate irq when DC rejects request * @test: The KUnit test context * - * With an OTG instance assigned but no DC attached, dc_interrupt_set() returns + * With an OTG instance assigned but no DC attached, amdgpu_dm_irq_set() returns * false and the function must report the request as busy (-EBUSY). */ static void dm_test_crtc_set_vupdate_irq_dc_busy(struct kunit *test) @@ -456,12 +456,12 @@ static void dm_test_crtc_set_vupdate_irq_dc_busy(struct kunit *test) acrtc->base.dev = &adev->ddev; acrtc->otg_inst = 0; - /* adev->dm.dc is NULL, so dc_interrupt_set() returns false. */ + /* adev->dm.dc is NULL, so amdgpu_dm_irq_set() returns false. */ KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true), -EBUSY); } -/* Per-source funcs let dc_interrupt_set() succeed without register access. */ +/* Per-source funcs let amdgpu_dm_irq_set() succeed without register access. */ static bool dm_test_vupdate_irq_src_set(struct irq_service *irq_service, const struct irq_source_info *info, bool enable) @@ -480,7 +480,7 @@ static struct irq_source_info_funcs dm_test_vupdate_irq_src_funcs = { .ack = dm_test_vupdate_irq_src_ack, }; -/* A .set that fails so dc_interrupt_set() reports the source as busy. */ +/* A .set that fails so amdgpu_dm_irq_set() reports the source as busy. */ static bool dm_test_vupdate_irq_src_set_busy(struct irq_service *irq_service, const struct irq_source_info *info, bool enable) @@ -522,7 +522,9 @@ static void dm_test_crtc_set_vupdate_irq_enable(struct kunit *test) irqs = kunit_kzalloc(test, sizeof(*irqs), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs); - /* Populate the per-source info table so dc_interrupt_set() succeeds. */ + /* + * Populate the per-source info table so amdgpu_dm_irq_set() succeeds. + */ info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info); @@ -1021,7 +1023,9 @@ static void dm_test_crtc_enable_vblank_vupdate_busy(struct kunit *test) irqs = kunit_kzalloc(test, sizeof(*irqs), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs); - /* Per-source .set fails so dc_interrupt_set() reports the source busy. */ + /* + * Per-source .set fails so amdgpu_dm_irq_set() reports the source busy. + */ info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c index ded0f680296f..51c3995de76d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c @@ -2594,17 +2594,21 @@ static void dm_test_is_dp_sink_present_null_priv(struct kunit *test) * dm_test_dmub_outbox_interrupt_control_null_dc - Test outbox irq control with NULL dc * @test: The KUnit test context * - * dc_interrupt_set() is NULL-safe and returns false when dc is NULL, so the + * amdgpu_dm_irq_set() is NULL-safe and returns false when dc is NULL, so the * helper returns false without touching real interrupt hardware. */ static void dm_test_dmub_outbox_interrupt_control_null_dc(struct kunit *test) { + struct amdgpu_device *adev; struct dc_context *ctx; + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx); + ctx->driver_context = adev; - /* ctx->dc is NULL → dc_interrupt_set returns false */ + /* adev->dm.dc is NULL → amdgpu_dm_irq_set returns false */ KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, true)); KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, false)); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c index af4eb29faeed..dc7d94f75ff4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c @@ -360,7 +360,7 @@ static bool dm_test_irq_src_ack(struct irq_service *irq_service, return true; } -/* Per-source funcs let dc_interrupt_set() succeed without register access. */ +/* Per-source funcs let amdgpu_dm_irq_set() succeed without register access. */ static struct irq_source_info_funcs dm_test_irq_src_funcs = { .set = dm_test_irq_src_set, .ack = dm_test_irq_src_ack, @@ -382,7 +382,7 @@ static struct dc *dm_test_alloc_dc_with_irq_service(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs); /* - * Populate the per-source info table so dc_interrupt_set()/_ack() + * Populate the per-source info table so amdgpu_dm_irq_set()/_ack() * succeed without touching hardware registers. */ info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER, @@ -1351,7 +1351,7 @@ static void dm_test_irq_suspend_empty(struct kunit *test) KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); /* - * With no registered handlers the HW dc_interrupt_set() calls are + * With no registered handlers the amdgpu_dm_irq_set() calls are * skipped, so suspend must complete without touching the (absent) DC. */ amdgpu_dm_irq_suspend(adev); @@ -1399,11 +1399,11 @@ static void dm_test_irq_resume_late_empty(struct kunit *test) } /** - * dm_test_irq_suspend_registered - Test suspend reaches the dc_interrupt_set path + * dm_test_irq_suspend_registered - Test suspend reaches the irq set path * @test: The KUnit test context * * Registers a low-context HPD handler so the handler list is non-empty, - * forcing amdgpu_dm_irq_suspend() to call dc_interrupt_set() (NULL-safe with + * forcing amdgpu_dm_irq_suspend() to call amdgpu_dm_irq_set() (NULL-safe with * no DC) and flush_work() on the registered handler. */ static void dm_test_irq_suspend_registered(struct kunit *test) @@ -1454,11 +1454,11 @@ static void dm_test_irq_suspend_disables_polling(struct kunit *test) } /** - * dm_test_irq_resume_early_registered - Test early resume reaches dc_interrupt_set + * dm_test_irq_resume_early_registered - Test early resume reaches irq set * @test: The KUnit test context * * Registers a low-context HPD RX handler so early resume calls - * dc_interrupt_set() for the short-pulse interrupt source. + * amdgpu_dm_irq_set() for the short-pulse interrupt source. */ static void dm_test_irq_resume_early_registered(struct kunit *test) { @@ -1482,10 +1482,10 @@ static void dm_test_irq_resume_early_registered(struct kunit *test) } /** - * dm_test_irq_resume_late_registered - Test late resume reaches dc_interrupt_set + * dm_test_irq_resume_late_registered - Test late resume reaches irq set * @test: The KUnit test context * - * Registers a low-context HPD handler so late resume calls dc_interrupt_set() + * Registers a low-context HPD handler so late resume calls amdgpu_dm_irq_set() * for the HPD interrupt source. */ static void dm_test_irq_resume_late_registered(struct kunit *test) @@ -1716,7 +1716,7 @@ static void dm_test_set_crtc_irq_state_enable(struct kunit *test) /* * otg_inst >= 0 computes the irq source and reaches the NULL-safe - * dc_interrupt_set(); the ips_support branch is skipped (dc == NULL). + * amdgpu_dm_irq_set(); the ips_support branch is skipped (dc == NULL). */ acrtc->otg_inst = 3; adev->mode_info.crtcs[0] = acrtc; @@ -1795,8 +1795,8 @@ static void dm_test_set_vupdate_irq_state_enable(struct kunit *test) * * With a non-NULL DC that advertises IPS support and currently allows idle * optimizations, dm_irq_state() must call dc_allow_idle_optimizations() before - * dc_interrupt_set(). disable_idle_power_optimizations makes that call a safe - * early return, and per-source stub funcs let dc_interrupt_set() succeed. + * amdgpu_dm_irq_set(). disable_idle_power_optimizations makes that call a safe + * early return, and per-source stub funcs let amdgpu_dm_irq_set() succeed. */ static void dm_test_set_crtc_irq_state_allows_idle(struct kunit *test) { @@ -2048,7 +2048,7 @@ static void dm_test_set_hpd_irq_state_null_dc(struct kunit *test) adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - /* dc_interrupt_set() is a no-op when dc is NULL, so both states + /* amdgpu_dm_irq_set() is a no-op when dc is NULL, so both states * return 0 without dereferencing the (absent) DC. */ KUNIT_EXPECT_EQ(test, amdgpu_dm_set_hpd_irq_state(adev, NULL, AMDGPU_HPD_1, @@ -2108,7 +2108,7 @@ static void dm_test_outbox_init_null_dc(struct kunit *test) adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - /* Single dc_interrupt_set() call must be skipped when dc is NULL. */ + /* Single amdgpu_dm_irq_set() call must be skipped when dc is NULL. */ amdgpu_dm_outbox_init(adev); } @@ -2126,7 +2126,7 @@ static void dm_test_hpd_init_empty_connectors(struct kunit *test) /* * With an empty connector list the per-connector loop is skipped and - * the initial clear loop relies on dc_interrupt_set() being a no-op + * the initial clear loop relies on amdgpu_dm_irq_set() being a no-op * for a NULL dc, so init must complete without touching the DC. */ amdgpu_dm_hpd_init(adev); @@ -2161,7 +2161,7 @@ static void dm_test_hpd_init_fini_with_connectors(struct kunit *test) /* * num_hpd = 0 forces irq_type >= num_hpd so the loop takes the HW - * fallback (dc_interrupt_set()) instead of amdgpu_irq_get(); with a + * fallback (amdgpu_dm_irq_set()) instead of amdgpu_irq_get(); with a * NULL dc that fallback is a safe no-op. */ adev->mode_info.num_hpd = 0; @@ -2247,7 +2247,7 @@ static void dm_test_hpd_init_fini_irq_ref(struct kunit *test) /* * num_hpd >= 1 makes irq_type (0) < num_hpd, so the loop takes the * amdgpu_irq_get()/amdgpu_irq_put() branch instead of the - * dc_interrupt_set() fallback. The mock device has irq.installed == + * amdgpu_dm_irq_set() fallback. The mock device has irq.installed == * false, so both calls fail early with -ENOENT (logging an error) * without touching the base-driver irq state. */ From 636417afb038849157b2a5dc55d75e63d19557bd Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Thu, 3 Sep 2026 14:40:00 -0400 Subject: [PATCH 062/114] drm/amd/display: Return success status from check_mode_supported [Description] dml21_top_utm_check_mode_supported must return the success status from the mode support result otherwise the caller will believe that mode support always returns true. This can cause issues where mode support returns true, where mode programming returns false. Reviewed-by: Wenjing Liu Signed-off-by: Alvin Lee Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c index 73e02b10934f..77c9308bb9e6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_utm.c @@ -290,7 +290,7 @@ static bool dml2_top_utm_check_mode_supported(struct dml2_check_mode_supported_i DML_LOG_INFO("%s exit with %s\n", __func__, dml2_status_str(status)); DML_LOG_TOP_IF_EXIT(); - return true; + return status == DML2_STATUS_OK; } static bool dml2_top_utm_build_mode_programming(struct dml2_build_mode_programming_in_out *in_out) From fd81d335f067cd8055710d08798c36557b5a6b91 Mon Sep 17 00:00:00 2001 From: Ovidiu Bunea Date: Thu, 3 Sep 2026 16:40:57 -0400 Subject: [PATCH 063/114] drm/amd/display: Add condition to skip MALL calculations if there is no MALL [Why & How] DCN42 has 0 bytes allocated for MALL, but this results in a 0 <= 0 comparison in CalculateMALLUseForStaticScreen(), which blindly returns true. This causes DML to calculate with is_using_mall_for_ss[n] = true. Example: 0 + 0 <= 0 -> true | v is_using_mall_for_ss[1] = true | v use_one_row_for_frame[1] = true PTE_BUFFER_MODE[1] = true | v dpte_row_height : 128 -> 808 dpte_row_width_ub : 196,608 -> 1,204,224 | +--> DST_Y_PER_PTE_ROW_NOM_L = 808 | +--> N_groups = ceil(1,204,224 / 65,536 / 2) = 10 | v REFCYC_PER_PTE_GROUP_NOM_L = 8416 (0x20E0) Currently, DML does not plumb out the programming for PTE_BUFFER_MODE and FORCE_ONE_ROW_FOR_FRAME, but even adding that programming does not enable the mode to work with these values. The simplest fix is to just block any opportunistic MALL calculations. Reviewed-by: Dillon Varone Signed-off-by: Ovidiu Bunea Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index 47d63dd69a90..64afb67054d5 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -2001,6 +2001,10 @@ static void CalculateMALLUseForStaticScreen( #endif } + /* If no MALL is allocated for DCN, there is no need to calculate usage for static screen. */ + if (MALLAllocatedForDCN == 0) + return; + SurfaceToAddToMALL = 0; CanAddAnotherSurfaceToMALL = true; while (CanAddAnotherSurfaceToMALL) { From d4e5df4d7ec5cf163077f389e85d454d996c7805 Mon Sep 17 00:00:00 2001 From: Ilya Bakoulin Date: Wed, 2 Sep 2026 16:43:41 -0400 Subject: [PATCH 064/114] drm/amd/display: Fix HDMI FRL audio enable [Why/How] The APG_CLOCK_EN bit can stay at 0 in some cases when going between DPMS off and DPMS on. Add APG_CLOCK_EN to audio_mute_control functions to make sure it gets programmed. Reviewed-by: Alvin Lee Signed-off-by: Ilya Bakoulin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 9 +++++++-- .../display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c index d4f66e62c729..43870d84ca2f 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c @@ -159,11 +159,16 @@ void hpo_enc42_audio_mute_control( struct hpo_frl_stream_encoder *enc, bool mute) { + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); ASSERT (enc->apg); - if (mute) + + if (mute) { enc->apg->funcs->disable_apg(enc->apg); - else + REG_UPDATE(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, 0); + } else { + REG_UPDATE(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, 1); enc->apg->funcs->enable_apg(enc->apg); + } } static const struct hpo_frl_stream_encoder_funcs dcn42_str_enc_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c index bded65896ef5..b5397d6760b1 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn60/dcn60_hpo_frl_stream_encoder.c @@ -36,11 +36,16 @@ static void hpo_enc60_audio_mute_control( struct hpo_frl_stream_encoder *enc, bool mute) { + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); ASSERT (enc->apg); - if (mute) + + if (mute) { enc->apg->funcs->disable_apg(enc->apg); - else + REG_UPDATE(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, 0); + } else { + REG_UPDATE(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, 1); enc->apg->funcs->enable_apg(enc->apg); + } } //Covered both, rounding up or rounding down from FRL Link Rate /18. From 0575f53925e832fba376583cd18667de8f31b13b Mon Sep 17 00:00:00 2001 From: James Lin Date: Thu, 13 Aug 2026 16:50:28 +0800 Subject: [PATCH 065/114] drm/amd/display: Cast DP DTO pixel clock math to avoid overflow and narrowing [Why] Commit 0421fc6ab3a8 ("drm/amd/display: fix __udivdi3 link error") switched get_dp_dto_frequency_100hz() and dcn401_get_dp_dto_frequency_100hz() to div_u64() but dropped the original explicit casts, leaving two issues: - In get_dp_dto_frequency_100hz(), "clock_hz * dp_dto_ref_khz * 10" is computed in 32-bit unsigned arithmetic (both operands are unsigned int) before being stored into the u64 temp, so the product can overflow before it is widened (flagged by Coverity OVERFLOW_BEFORE_WIDEN). - div_u64() returns a u64 that is assigned directly to the unsigned int *pixel_clk_100hz, an implicit narrowing conversion that trips -Wconversion (possible loss of data) on stricter builds. [How] Cast clock_hz to unsigned long long so the multiplication is performed in 64-bit, and make the u64 -> unsigned int narrowing explicit with an (unsigned int) cast on the div_u64() results in both functions. The computed values are unchanged. Fixes: 0421fc6ab3a8 ("drm/amd/display: fix __udivdi3 link error") Reviewed-by: Wayne Lin Signed-off-by: James Lin Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 20ccecf23677..f88404a37dea 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1232,9 +1232,9 @@ static bool get_dp_dto_frequency_100hz( */ modulo_hz = REG_READ(MODULO[inst]); if (modulo_hz) { - temp = clock_hz * dp_dto_ref_khz * 10; + temp = (unsigned long long)clock_hz * dp_dto_ref_khz * 10; ASSERT(temp <= UINT_MAX * modulo_hz * 100ULL); - *pixel_clk_100hz = div_u64(temp, modulo_hz * 100); + *pixel_clk_100hz = (unsigned int)div_u64(temp, modulo_hz * 100); } else *pixel_clk_100hz = 0; } else { @@ -1293,7 +1293,7 @@ static bool dcn401_get_dp_dto_frequency_100hz(const struct clock_source *clock_s BREAK_TO_DEBUGGER(); *pixel_clk_100hz = 0; } else { - *pixel_clk_100hz = div_u64(temp, 100); + *pixel_clk_100hz = (unsigned int)div_u64(temp, 100); } return true; From c615f0715b1493f5c241cd82bbe239c196a89a14 Mon Sep 17 00:00:00 2001 From: Tom Chung Date: Mon, 2 Feb 2026 16:06:40 +0800 Subject: [PATCH 066/114] drm/amd/display: Add inbox0 HW lock helpers for DCN35 Add dcn35_dmub_hw_control_lock() and dcn35_dmub_hw_control_lock_fast(), a SW lock mechanism to prevent racing between driver and FW. The helpers are not wired into dcn35_funcs/dcn351_funcs yet, so there is no functional change: DCN3.5/3.5.1/3.6 keep using the existing inbox1 lock path. Enabling them will come in a follow-up patch. Reviewed-by: Ray Wu Signed-off-by: Tom Chung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 43 +++++++++++++++++++ .../amd/display/dc/hwss/dcn35/dcn35_hwseq.h | 4 ++ 2 files changed, 47 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 942d3f3d9f25..a0944d53860e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -1822,3 +1822,46 @@ void dcn35_disable_link_output(struct dc_link *link, dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY); } + +void dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) +{ + /* use always for now */ + union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; + + if (!dc->ctx || !dc->ctx->dmub_srv) + return; + + /* if not support inbox0 lock, would not use inbox0 lock mechanism */ + if (!dc->ctx->dmub_srv->dmub->meta_info.feature_bits.bits.inbox0_lock_support) + return; + + if (!dc_dmub_srv_is_cursor_offload_enabled(dc) && + !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) + return; + + hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; + hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; + hw_lock_cmd.bits.lock = lock; + hw_lock_cmd.bits.should_release = !lock; + dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); +} + +void dcn35_dmub_hw_control_lock_fast(union block_sequence_params *params) +{ + struct dc *dc = params->dmub_hw_control_lock_fast_params.dc; + bool lock = params->dmub_hw_control_lock_fast_params.lock; + + /* if not support inbox0 lock, would not use inbox0 lock mechanism */ + if (!dc->ctx->dmub_srv->dmub->meta_info.feature_bits.bits.inbox0_lock_support) + return; + + if (params->dmub_hw_control_lock_fast_params.is_required) { + union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; + + hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; + hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; + hw_lock_cmd.bits.lock = lock; + hw_lock_cmd.bits.should_release = !lock; + dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); + } +} diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h index fce9acb5c2fc..392ccbcb343c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h @@ -27,6 +27,8 @@ #ifndef __DC_HWSS_DCN35_H__ #define __DC_HWSS_DCN35_H__ +#include "inc/core_types.h" +#include "dc.h" #include "hw_sequencer_private.h" struct dc; @@ -115,5 +117,7 @@ void dcn35_program_cursor_offload_now(struct dc *dc, const struct pipe_ctx *pipe void dcn35_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal); +void dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); +void dcn35_dmub_hw_control_lock_fast(union block_sequence_params *params); #endif /* __DC_HWSS_DCN35_H__ */ From 30ca154558c8aef2eb9bdc8a8e8fcd3e41b6ce7e Mon Sep 17 00:00:00 2001 From: Rafal Ostrowski Date: Thu, 7 May 2026 11:17:30 +0200 Subject: [PATCH 067/114] drm/amd/display: Unify fast update classification paths [Why] The dc_fast_update intermediate struct created code duplication and complexity with multiple classification paths (populate_fast_updates, fast_nonaddr_updates_exist, full_update_required). This refactoring simplifies the update classification system by consolidating to a single path while maintaining compatibility. [How] Remove entire dc_fast_update struct and associated helper functions: - populate_fast_updates - fast_nonaddr_updates_exist - full_update_required Refactor check_update_surfaces_for_stream as the single classification path with explicit handling for func_shaper, lut3d_func, cursor_csc_color_matrix_change, and scaler_sharpener_update. Add a reserved bitfield to the stream_update_flags union for completeness guards. Extract dc_check_address_only_update and dc_check_update_surfaces_for_stream as public. Reviewed-by: Alvin Lee Signed-off-by: Rafal Ostrowski Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 246 ++---------------- drivers/gpu/drm/amd/display/dc/dc.h | 31 --- drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 + .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 17 +- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.h | 2 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 12 +- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 2 +- .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 13 +- .../amd/display/dc/hwss/dcn42/dcn42_hwseq.h | 2 +- .../drm/amd/display/dc/hwss/hw_sequencer.h | 2 +- 10 files changed, 58 insertions(+), 272 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 171d8bb24844..8839568e2adc 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2278,6 +2278,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c struct dc_stream_state *dc_streams[MAX_STREAMS] = {0}; struct dc_state *old_state; bool subvp_prev_use = false; + bool dmub_locked = false; dc_z10_restore(dc); dc_allow_idle_optimizations(dc, false); @@ -2329,7 +2330,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c if (dc->hwss.subvp_pipe_control_lock) dc->hwss.subvp_pipe_control_lock(dc, context, true, true, NULL, subvp_prev_use); if (dc->hwss.dmub_hw_control_lock) - dc->hwss.dmub_hw_control_lock(dc, context, true); + dmub_locked = dc->hwss.dmub_hw_control_lock(dc, context, true); if (dc->hwss.update_dsc_pg) dc->hwss.update_dsc_pg(dc, context, false); @@ -2405,7 +2406,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c dc->hwss.commit_subvp_config(dc, context); if (dc->hwss.subvp_pipe_control_lock) dc->hwss.subvp_pipe_control_lock(dc, context, false, true, NULL, subvp_prev_use); - if (dc->hwss.dmub_hw_control_lock) + if (dc->hwss.dmub_hw_control_lock && dmub_locked) dc->hwss.dmub_hw_control_lock(dc, context, false); for (i = 0; i < context->stream_count; i++) { @@ -3183,8 +3184,10 @@ static struct dc_update_descriptor check_update_surfaces_for_stream( stream_update->vrr_active_variable || stream_update->vrr_active_fixed)) su_flags->bits.fams_changed = 1; - if (stream_update->scaler_sharpener_update) + if (stream_update->scaler_sharpener_update) { su_flags->bits.scaler_sharpener = 1; + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM); + } if (stream_update->sharpening_required) su_flags->bits.sharpening_required = 1; @@ -3250,6 +3253,16 @@ static struct dc_update_descriptor check_update_surfaces_for_stream( su_flags->bits.cursor_pos = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } + + if (stream_update->func_shaper) { + su_flags->bits.func_shaper = 1; + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM); + } + + if (stream_update->lut3d_func) { + su_flags->bits.lut3d_func = 1; + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM); + } } for (int i = 0 ; i < surface_count; i++) { @@ -4883,6 +4896,7 @@ static void commit_planes_for_stream(struct dc *dc, bool should_lock_all_pipes = (update_type != UPDATE_TYPE_FAST); bool subvp_prev_use = false; bool subvp_curr_use = false; + bool dmub_locked = false; uint8_t current_stream_mask = 0; if (should_lock_all_pipes) @@ -4988,7 +5002,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, NULL, subvp_prev_use); if (dc->hwss.dmub_hw_control_lock) - dc->hwss.dmub_hw_control_lock(dc, context, true); + dmub_locked = dc->hwss.dmub_hw_control_lock(dc, context, true); dc->hwss.interdependent_update_lock(dc, context, true); } else { @@ -4996,7 +5010,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use); if (dc->hwss.dmub_hw_control_lock) - dc->hwss.dmub_hw_control_lock(dc, context, true); + dmub_locked = dc->hwss.dmub_hw_control_lock(dc, context, true); /* Lock the top pipe while updating plane addrs, since freesync requires * plane addr update event triggers to be synchronized. @@ -5039,7 +5053,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use); - if (dc->hwss.dmub_hw_control_lock) + if (dc->hwss.dmub_hw_control_lock && dmub_locked) dc->hwss.dmub_hw_control_lock(dc, context, false); return; } @@ -5275,12 +5289,12 @@ static void commit_planes_for_stream(struct dc *dc, if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) { if (dc->hwss.subvp_pipe_control_lock) dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use); - if (dc->hwss.dmub_hw_control_lock) + if (dc->hwss.dmub_hw_control_lock && dmub_locked) dc->hwss.dmub_hw_control_lock(dc, context, false); } else { if (dc->hwss.subvp_pipe_control_lock) dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use); - if (dc->hwss.dmub_hw_control_lock) + if (dc->hwss.dmub_hw_control_lock && dmub_locked) dc->hwss.dmub_hw_control_lock(dc, context, false); } @@ -5739,127 +5753,6 @@ static bool commit_minimal_transition_state(struct dc *dc, return true; } -void populate_fast_updates(struct dc_fast_update *fast_update, - struct dc_surface_update *srf_updates, - int surface_count, - struct dc_stream_update *stream_update) -{ - int i = 0; - - if (stream_update) { - fast_update[0].out_transfer_func = stream_update->out_transfer_func; - fast_update[0].output_csc_transform = stream_update->output_csc_transform; - fast_update[0].cursor_attributes = stream_update->cursor_attributes; - fast_update[0].cursor_position = stream_update->cursor_position; - fast_update[0].periodic_interrupt = stream_update->periodic_interrupt; - fast_update[0].dither_option = stream_update->dither_option; - fast_update[0].gamut_remap = stream_update->gamut_remap; - fast_update[0].vrr_infopacket = stream_update->vrr_infopacket; - fast_update[0].vsc_infopacket = stream_update->vsc_infopacket; - fast_update[0].vsp_infopacket = stream_update->vsp_infopacket; - fast_update[0].hfvsif_infopacket = stream_update->hfvsif_infopacket; - fast_update[0].vtem_infopacket = stream_update->vtem_infopacket; - fast_update[0].adaptive_sync_infopacket = stream_update->adaptive_sync_infopacket; - fast_update[0].avi_infopacket = stream_update->avi_infopacket; - fast_update[0].hdr_static_metadata = stream_update->hdr_static_metadata; - } else { - fast_update[0].out_transfer_func = NULL; - fast_update[0].output_csc_transform = NULL; - fast_update[0].cursor_attributes = NULL; - fast_update[0].cursor_position = NULL; - fast_update[0].periodic_interrupt = NULL; - fast_update[0].dither_option = NULL; - fast_update[0].gamut_remap = NULL; - fast_update[0].vrr_infopacket = NULL; - fast_update[0].vsc_infopacket = NULL; - fast_update[0].vsp_infopacket = NULL; - fast_update[0].hfvsif_infopacket = NULL; - fast_update[0].vtem_infopacket = NULL; - fast_update[0].adaptive_sync_infopacket = NULL; - fast_update[0].avi_infopacket = NULL; - fast_update[0].hdr_static_metadata = NULL; - } - - for (i = 0; i < surface_count; i++) { - fast_update[i].flip_addr = srf_updates[i].flip_addr; - fast_update[i].gamma = srf_updates[i].gamma; - fast_update[i].gamut_remap_matrix = srf_updates[i].gamut_remap_matrix; - fast_update[i].input_csc_color_matrix = srf_updates[i].input_csc_color_matrix; - fast_update[i].coeff_reduction_factor = srf_updates[i].coeff_reduction_factor; - fast_update[i].cursor_csc_color_matrix = srf_updates[i].cursor_csc_color_matrix; - fast_update[i].cm_hist_control = srf_updates[i].cm_hist_control; - } -} - -static bool fast_updates_exist(const struct dc_fast_update *fast_update, int surface_count) -{ - int i; - - if (fast_update[0].out_transfer_func || - fast_update[0].output_csc_transform || - fast_update[0].cursor_attributes || - fast_update[0].cursor_position || - fast_update[0].periodic_interrupt || - fast_update[0].dither_option || - fast_update[0].gamut_remap || - fast_update[0].vrr_infopacket || - fast_update[0].vsc_infopacket || - fast_update[0].vsp_infopacket || - fast_update[0].hfvsif_infopacket || - fast_update[0].vtem_infopacket || - fast_update[0].adaptive_sync_infopacket || - fast_update[0].avi_infopacket || - fast_update[0].hdr_static_metadata) - return true; - - for (i = 0; i < surface_count; i++) { - if (fast_update[i].flip_addr || - fast_update[i].gamma || - fast_update[i].gamut_remap_matrix || - fast_update[i].input_csc_color_matrix || - fast_update[i].cursor_csc_color_matrix || - fast_update[i].cm_hist_control || - fast_update[i].coeff_reduction_factor) - return true; - } - - return false; -} - -bool fast_nonaddr_updates_exist(struct dc_fast_update *fast_update, int surface_count) -{ - int i; - - if (fast_update[0].out_transfer_func || - fast_update[0].output_csc_transform || - fast_update[0].gamut_remap || - fast_update[0].cursor_attributes || - fast_update[0].cursor_position || - fast_update[0].periodic_interrupt || - fast_update[0].dither_option || - fast_update[0].vrr_infopacket || - fast_update[0].vsc_infopacket || - fast_update[0].vsp_infopacket || - fast_update[0].hfvsif_infopacket || - fast_update[0].vtem_infopacket || - fast_update[0].adaptive_sync_infopacket || - fast_update[0].avi_infopacket || - fast_update[0].hdr_static_metadata) - return true; - - for (i = 0; i < surface_count; i++) { - if (fast_update[i].input_csc_color_matrix || - fast_update[i].gamma || - fast_update[i].gamut_remap_matrix || - fast_update[i].coeff_reduction_factor || - fast_update[i].cm_hist_control || - fast_update[i].cursor_csc_color_matrix) - return true; - } - - return false; -} - static bool full_update_required_weak( const struct dc *dc, const struct dc_surface_update *srf_updates, @@ -5888,67 +5781,6 @@ static bool full_update_required_weak( return false; } -static bool full_update_required( - const struct dc *dc, - const struct dc_surface_update *srf_updates, - int surface_count, - const struct dc_stream_update *stream_update, - const struct dc_stream_state *stream) -{ - if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream)) - return true; - - for (int i = 0; i < surface_count; i++) { - if (srf_updates && - (srf_updates[i].plane_info || - srf_updates[i].scaling_info || - (srf_updates[i].hdr_mult.value && - srf_updates[i].hdr_mult.value != srf_updates->surface->hdr_mult.value) || - (srf_updates[i].sdr_white_level_nits && - srf_updates[i].sdr_white_level_nits != srf_updates->surface->sdr_white_level_nits) || - srf_updates[i].in_transfer_func || - srf_updates[i].surface->force_full_update || - (srf_updates[i].flip_addr && - srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface))) - return true; - } - - if (stream_update && - (((stream_update->src.height != 0 && stream_update->src.width != 0) || - (stream_update->dst.height != 0 && stream_update->dst.width != 0) || - stream_update->integer_scaling_update) || - stream_update->abm_level || - stream_update->dpms_off || - stream_update->allow_freesync || - stream_update->vrr_active_variable || - stream_update->vrr_active_fixed || - stream_update->output_color_space || - stream_update->wb_update || - stream_update->dsc_config || - stream_update->mst_bw_update || - stream_update->func_shaper || - stream_update->lut3d_func || - stream_update->pending_test_pattern || - stream_update->crtc_timing_adjust || - stream_update->scaler_sharpener_update || - stream_update->hw_cursor_req)) - return true; - - return false; -} - -static bool fast_update_only( - const struct dc *dc, - const struct dc_fast_update *fast_update, - const struct dc_surface_update *srf_updates, - int surface_count, - const struct dc_stream_update *stream_update, - const struct dc_stream_state *stream) -{ - return fast_updates_exist(fast_update, surface_count) - && !full_update_required(dc, srf_updates, surface_count, stream_update, stream); -} - static bool update_planes_and_stream_v2(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, @@ -5956,7 +5788,6 @@ static bool update_planes_and_stream_v2(struct dc *dc, { struct dc_state *context; enum dc_update_type update_type; - struct dc_fast_update fast_update[MAX_SURFACES] = {0}; /* In cases where MPO and split or ODM are used transitions can * cause underflow. Apply stream configuration with minimal pipe @@ -5964,11 +5795,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, */ bool force_minimal_pipe_splitting = 0; bool is_plane_addition = 0; - bool is_fast_update_only; - populate_fast_updates(fast_update, srf_updates, surface_count, stream_update); - is_fast_update_only = fast_update_only(dc, fast_update, srf_updates, - surface_count, stream_update, stream); force_minimal_pipe_splitting = could_mpcc_tree_change_for_active_pipes( dc, stream, @@ -6006,7 +5833,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, commit_minimal_transition_state_in_dc_update(dc, context, stream, srf_updates, surface_count); - if (is_fast_update_only && !dc->check_config.enable_legacy_fast_update) { + if (update_type == UPDATE_TYPE_FAST && !dc->check_config.enable_legacy_fast_update) { commit_planes_for_stream_fast(dc, srf_updates, surface_count, @@ -6042,13 +5869,8 @@ static void commit_planes_and_stream_update_on_current_context(struct dc *dc, struct dc_stream_update *stream_update, enum dc_update_type update_type) { - struct dc_fast_update fast_update[MAX_SURFACES] = {0}; - ASSERT(update_type < UPDATE_TYPE_FULL); - populate_fast_updates(fast_update, srf_updates, surface_count, - stream_update); - if (fast_update_only(dc, fast_update, srf_updates, surface_count, - stream_update, stream) && + if (update_type == UPDATE_TYPE_FAST && !dc->check_config.enable_legacy_fast_update) commit_planes_for_stream_fast(dc, srf_updates, @@ -8244,23 +8066,6 @@ static bool update_planes_and_stream_prepare_v3( ASSERT(scratch->flow == UPDATE_V3_FLOW_INVALID); dc_exit_ips_for_hw_access(scratch->dc); - /* HWSS path determination needs to be done prior to updating the surface and stream states. */ - struct dc_fast_update fast_update[MAX_SURFACES] = { 0 }; - - populate_fast_updates(fast_update, - scratch->surface_updates, - scratch->surface_count, - scratch->stream_update); - - const bool is_hwss_fast_path_only = - fast_update_only(scratch->dc, - fast_update, - scratch->surface_updates, - scratch->surface_count, - scratch->stream_update, - scratch->stream) && - !scratch->dc->check_config.enable_legacy_fast_update; - if (!update_planes_and_stream_state( scratch->dc, scratch->surface_updates, @@ -8276,7 +8081,8 @@ static bool update_planes_and_stream_prepare_v3( if (scratch->new_context == scratch->dc->current_state) { ASSERT(scratch->update_type < UPDATE_TYPE_FULL); - scratch->flow = is_hwss_fast_path_only + scratch->flow = (scratch->update_type == UPDATE_TYPE_FAST && + !scratch->dc->check_config.enable_legacy_fast_update) ? UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST : UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL; return true; diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 1e25ed4eea80..634c0b7686bb 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -2091,32 +2091,6 @@ struct dc_scaling_info { struct scaling_taps scaling_quality; }; -struct dc_fast_update { - const struct dc_flip_addrs *flip_addr; - const struct dc_gamma *gamma; - const struct colorspace_transform *gamut_remap_matrix; - const struct dc_csc_transform *input_csc_color_matrix; - const struct fixed31_32 *coeff_reduction_factor; - struct dc_transfer_func *out_transfer_func; - struct dc_csc_transform *output_csc_transform; - const struct dc_csc_transform *cursor_csc_color_matrix; - struct cm_hist_control *cm_hist_control; - /* stream-level fast updates */ - const struct colorspace_transform *gamut_remap; - const struct dc_cursor_attributes *cursor_attributes; - const struct dc_cursor_position *cursor_position; - const struct periodic_interrupt_config *periodic_interrupt; - const enum dc_dither_option *dither_option; - struct dc_info_packet *vrr_infopacket; - struct dc_info_packet *vsc_infopacket; - struct dc_info_packet *vsp_infopacket; - struct dc_info_packet *hfvsif_infopacket; - struct dc_info_packet *vtem_infopacket; - struct dc_info_packet *adaptive_sync_infopacket; - struct dc_info_packet *avi_infopacket; - struct dc_info_packet *hdr_static_metadata; -}; - struct dc_surface_update { struct dc_plane_state *surface; @@ -2422,11 +2396,6 @@ bool dc_resource_is_dsc_encoding_supported(const struct dc *dc); void get_audio_check(struct audio_info *aud_modes, struct audio_check *aud_chk); -bool fast_nonaddr_updates_exist(struct dc_fast_update *fast_update, int surface_count); -void populate_fast_updates(struct dc_fast_update *fast_update, - struct dc_surface_update *srf_updates, - int surface_count, - struct dc_stream_update *stream_update); /* * Set up streams and links associated to drive sinks * The streams parameter is an absolute set of all active streams. diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index d9d79788a92c..fec5e7a84bb9 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -123,6 +123,9 @@ union stream_update_flags { uint32_t info_frame : 1; uint32_t dmdata : 1; uint32_t dither : 1; + uint32_t func_shaper : 1; + uint32_t lut3d_func : 1; + uint32_t reserved : 11; /* adjust when adding new flags */ } bits; uint32_t raw; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index a0944d53860e..f9d8ff6ce010 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -1823,27 +1823,30 @@ void dcn35_disable_link_output(struct dc_link *link, dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY); } -void dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) +bool dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) { - /* use always for now */ union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; if (!dc->ctx || !dc->ctx->dmub_srv) - return; + return false; /* if not support inbox0 lock, would not use inbox0 lock mechanism */ if (!dc->ctx->dmub_srv->dmub->meta_info.feature_bits.bits.inbox0_lock_support) - return; + return false; - if (!dc_dmub_srv_is_cursor_offload_enabled(dc) && - !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) - return; + if (lock) { + if (!dc_dmub_srv_is_cursor_offload_enabled(dc) && + !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) + return false; + } hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; hw_lock_cmd.bits.lock = lock; hw_lock_cmd.bits.should_release = !lock; dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); + + return true; } void dcn35_dmub_hw_control_lock_fast(union block_sequence_params *params) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h index 392ccbcb343c..9f779e408ec6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h @@ -117,7 +117,7 @@ void dcn35_program_cursor_offload_now(struct dc *dc, const struct pipe_ctx *pipe void dcn35_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal); -void dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); +bool dcn35_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); void dcn35_dmub_hw_control_lock_fast(union block_sequence_params *params); #endif /* __DC_HWSS_DCN35_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index c76754df2c38..8f202206c3c4 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -1556,25 +1556,27 @@ void dcn401_optimize_bandwidth_sequence(struct dc *dc, dc->clk_mgr, context, true, seq_state); } -void dcn401_dmub_hw_control_lock(struct dc *dc, +bool dcn401_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) { (void)context; - /* use always for now */ union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; if (!dc->ctx || !dc->ctx->dmub_srv) - return; + return false; - if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc)) - return; + if (lock) { + if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc)) + return false; + } hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; hw_lock_cmd.bits.lock = lock; hw_lock_cmd.bits.should_release = !lock; dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); + return true; } void dcn401_dmub_hw_control_lock_fast(union block_sequence_params *params) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h index 81e3700d728b..43bea09fb205 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h @@ -78,7 +78,7 @@ void dcn401_optimize_bandwidth_sequence(struct dc *dc, struct dc_state *context, struct block_sequence_state *seq_state); -void dcn401_dmub_hw_control_lock(struct dc *dc, +bool dcn401_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); void dcn401_fams2_update_config(struct dc *dc, struct dc_state *context, bool enable); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index c152941c34f9..ee40ee8f62ef 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -1188,25 +1188,28 @@ void dcn42_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) return; } -void dcn42_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) +bool dcn42_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) { union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; if (!dc->ctx || !dc->ctx->dmub_srv) - return; + return false; /* Use helper to check PSR/Replay for all streams in context */ - if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc) - && !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) - return; + if (lock) { + if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc) + && !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) + return false; + } hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; hw_lock_cmd.bits.lock = lock; hw_lock_cmd.bits.should_release = !lock; dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); + return true; } void dcn42_dmub_hw_control_lock_fast(union block_sequence_params *params) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h index 95b8ce23286c..31b92274a2f5 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h @@ -43,7 +43,7 @@ void dcn42_hw_block_power_up(struct dc *dc, struct pg_block_update *update_state); void dcn42_root_clock_control(struct dc *dc, struct pg_block_update *update_state, bool power_on); -void dcn42_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); +bool dcn42_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); void dcn42_dmub_hw_control_lock_fast(union block_sequence_params *params); void dcn42_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc); void dcn42_power_down_on_boot(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index f023bef4fd7e..fc5935b1e8b7 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -1719,7 +1719,7 @@ struct hw_sequencer_funcs { const struct dc_state *cur_ctx, const struct dc_state *new_ctx); void (*wait_for_dcc_meta_propagation)(uint32_t delay); - void (*dmub_hw_control_lock)(struct dc *dc, + bool (*dmub_hw_control_lock)(struct dc *dc, struct dc_state *context, bool lock); void (*fams2_update_config)(struct dc *dc, From fb8d8c5b00cf00c35944a1f046a2f1a3551546b3 Mon Sep 17 00:00:00 2001 From: Chenyu Chen Date: Mon, 7 Sep 2026 18:43:55 +0800 Subject: [PATCH 068/114] drm/amd/display: Use unsigned types for FRL cap check params and HPO read_state [Why] Commit c3fb1fb9e65f ("drm/amd/display: Fix warnings") aligned the signedness of a large number of DC values, but two hunks of the original change were not carried over: - struct frl_cap_chk_params_fixed31_32 still declares audio_packet_type, h_active and h_blank as int. All three only ever hold non-negative values: h_active and h_blank are HDMI timing quantities counted in pixels, and audio_packet_type is only compared against the positive HDMI audio packet type constants 0x02, 0x07, 0x08, 0x09 and 0x0e in frl_capacity_computations_common(). - hpo_enc3_read_state() still declares pixel_encoding, color_depth and odm_combine as int and passes their addresses to REG_GET_2()/REG_GET(), whose generic_reg_get*() backends take uint32_t *. The mismatch is only hidden by the explicit (uint32_t *) cast inside the REG_GET macros. The DCN401 equivalent, hpo_enc401_read_state(), already uses uint32_t. [How] Widen the three struct fields to unsigned int/uint32_t and the three locals to uint32_t so the types match how the values are produced and consumed. No computed value changes. Reviewed-by: Alex Hung Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 6 +++--- .../amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 634c0b7686bb..b6c71ebb7ea7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -112,10 +112,10 @@ struct frl_cap_chk_params_fixed31_32 { int lanes; struct fixed31_32 f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */ struct fixed31_32 r_bit_nominal; /* FRL bitrate (bps) */ - int audio_packet_type; + unsigned int audio_packet_type; struct fixed31_32 f_audio; /* Audio rate (Hz) */ - int h_active; /* Active pixels per line */ - int h_blank; /* Blanking pixels per line */ + uint32_t h_active; /* Active pixels per line */ + uint32_t h_blank; /* Blanking pixels per line */ int bpc; /* Bits per component */ int vic; /* Video Identification Code */ diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c index cff5c95a771c..f4c21998d845 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c @@ -921,9 +921,9 @@ void hpo_enc3_read_state( struct hpo_frl_stream_encoder *enc, struct hpo_frl_stream_encoder_state *state) { - int pixel_encoding; - int color_depth; - int odm_combine; + uint32_t pixel_encoding; + uint32_t color_depth; + uint32_t odm_combine; struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); ASSERT(state); From c3c902263f134e3a0299f7c3355bc1f261541562 Mon Sep 17 00:00:00 2001 From: Taimur Hassan Date: Thu, 3 Sep 2026 21:59:13 -0500 Subject: [PATCH 069/114] drm/amd/display: Promote DC to 3.2.398 This version brings along the following updates: - Expand amdgpu_dm KUnit coverage across connector init, sink stream creation, FreeSync caps, forced atomic commit, plane modifiers, cursor updates, panic flush, MST connector creation, AUX transfers, link bandwidth readback and MST DSC configuration. - Refactor RMCM into a separate module. - Decouple cursor offload hwss executors from pipe context. - Decouple HUBP_UPDATE_PLANE_ADDR from pipe_ctx. - Rename lock_and_validation_needed to needs_dc_state_realloc. - Drop the dead update_type param from update_planes_and_stream_adapter. - Attach only plane updates and stream updates that actually changed. - Unify fast update classification paths. - Request DMUB HW cursor offload. - Program DCC as part of address update. - Update LLS and UPSP programming paths. - Add lock-free memory pool. - Add instance field to struct mpc and struct dccg. - Cleanup DMUB command submission interfaces. - Add inbox0 HW lock helpers for DCN35. - Atomize IRQ register read/modify/write ops. - Add Replay cumulative residency query. - Add urgent assertion counter probe. - Add debug option to force optional UCLK support. - Force DSC to 8bpp for SST and MST DP tunneling over USB4. - Honor forced RGB pixel encoding. - Add option for certain panels to disable FEC. - Cap DML2.1 vmin ODM combine at 2:1 for eDP. - Add is_odm_enabled callback to skip init_odm on active ODM pipes. - Remove MALL capabilities from DCN42B. - Skip MALL calculations when there is no MALL. - Enable power gating on dcn42b. - Remove SDPIF_PORT_CONTROL programming for DCN31/35/42. - Enable back alt-ch. - Flush ISM work before releasing the stream. - Fix HDMI FRL audio enable. - Fix peak bandwidth measurement sequence. - Bound the DSC power gating loop by num_dsc. - Cast DP DTO pixel clock math to avoid overflow and narrowing. - Use unsigned types for FRL cap check params and HPO read_state. - Return success status from check_mode_supported. - Add SPDX license identifier to dcn30_dpp_cm.c. Acked-by: Tom Chung Signed-off-by: Taimur Hassan Signed-off-by: Chenyu Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index b6c71ebb7ea7..5174a0098e75 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -67,7 +67,7 @@ struct dcn_dsc_reg_state; struct dcn_optc_reg_state; struct dcn_dccg_reg_state; -#define DC_VER "3.2.397" +#define DC_VER "3.2.398" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC From 3d5f28824d48fcfc9636075c5074baed88a5e71a Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 20 Jul 2026 15:44:06 -0400 Subject: [PATCH 070/114] drm/amdgpu: Add trace events for pid register/deregister Some debugging applications need to track when a process starts or stops using the amdgpu driver. For those processes, add ftrace events that track when a amdgpu_vm task info is changed or destroyed. v2: Deregister in fini, not destroy. v3: Also report render minor and major cc: Sunil Khatri Reviewed-by: Alex Deucher Signed-off-by: David Francis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 33 +++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 26 ++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h index 09dd3174d505..66f4a1872913 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h @@ -730,6 +730,39 @@ TRACE_EVENT(amdgpu_userq_state_changed, __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->to) ); +TRACE_EVENT(amdgpu_register_pid, + TP_PROTO(uint32_t pid, int major, int minor), + TP_ARGS(pid, major, minor), + TP_STRUCT__entry( + __field(u32, pid) + __field(int, major) + __field(int, minor) + ), + TP_fast_assign( + __entry->pid = pid; + __entry->major = major; + __entry->minor = minor; + ), + TP_printk("amdgpu register pid %d render %d:%d", + __entry->pid, __entry->major, __entry->minor) +); +TRACE_EVENT(amdgpu_deregister_pid, + TP_PROTO(uint32_t pid, int major, int minor), + TP_ARGS(pid, major, minor), + TP_STRUCT__entry( + __field(u32, pid) + __field(int, major) + __field(int, minor) + ), + TP_fast_assign( + __entry->pid = pid; + __entry->major = major; + __entry->minor = minor; + ), + TP_printk("amdgpu deregister pid %d render %d:%d", + __entry->pid, __entry->major, __entry->minor) +); + #undef AMDGPU_JOB_GET_TIMELINE_NAME #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 36f6dcd063fe..6b210f7af57b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include "amdgpu.h" @@ -2563,6 +2564,12 @@ static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm) return 0; } +static void amdgpu_vm_render_devt(struct amdgpu_device *adev, int *major, int *minor) +{ + *major = DRM_MAJOR; + *minor = adev_to_drm(adev)->render->index; +} + /** * amdgpu_vm_set_task_info - Sets VMs task info. * @@ -2576,6 +2583,18 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm) if (vm->task_info->task.pid == current->pid) return; + if (vm->root.bo) { + struct amdgpu_device *adev = amdgpu_ttm_adev(vm->root.bo->tbo.bdev); + int major, minor; + + amdgpu_vm_render_devt(adev, &major, &minor); + + if (vm->task_info->task.pid) + trace_amdgpu_deregister_pid(vm->task_info->task.pid, + major, minor); + trace_amdgpu_register_pid(current->pid, major, minor); + } + vm->task_info->task.pid = current->pid; get_task_comm(vm->task_info->task.comm, current); @@ -2876,6 +2895,13 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) ti->process_name, ti->task.pid, ti->task.comm, ti->tgid); } + if (vm->task_info && vm->task_info->task.pid) { + int major, minor; + + amdgpu_vm_render_devt(adev, &major, &minor); + trace_amdgpu_deregister_pid(vm->task_info->task.pid, major, minor); + } + amdgpu_vm_put_task_info(vm->task_info); } From 940850827f703bea3be044b12eccf904c93b8ff8 Mon Sep 17 00:00:00 2001 From: David Belanger Date: Fri, 21 Aug 2026 10:25:25 -0400 Subject: [PATCH 071/114] drm/kfd: Add CU occupancy support to GFX10 and GFX10.3 Port changes from GFX9 to GFX10/GFX10.3 mostly as-is. Minor changes to register access code. Assisted-by: Claude:sonnet-4-6 Reviewed-by: Alex Deucher Signed-off-by: David Belanger Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c | 148 ++++++++++++++++++ .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h | 3 + .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c | 1 + 3 files changed, 152 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c index bc48b2ba30d1..1550e275f0ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c @@ -1091,6 +1091,153 @@ uint32_t kgd_gfx_v10_hqd_sdma_get_doorbell(struct amdgpu_device *adev, return 0; } +static void lock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_lock(&adev->srbm_mutex); + mutex_lock(&adev->grbm_idx_mutex); + +} + +static void unlock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_unlock(&adev->grbm_idx_mutex); + mutex_unlock(&adev->srbm_mutex); +} + +/** + * get_wave_count: Read device registers to get number of waves in flight for + * a particular queue. The method also returns the doorbell offset associated + * with the queue. + * + * @adev: Handle of device whose registers are to be read + * @queue_idx: Index of queue in the queue-map bit-field + * @queue_cnt: Stores the wave count and doorbell offset for an active queue + * @inst: xcc's instance number on a multi-XCC setup + */ +static void get_wave_count(struct amdgpu_device *adev, int queue_idx, + struct kfd_cu_occupancy *queue_cnt, uint32_t inst) +{ + int pipe_idx; + int queue_slot; + unsigned int reg_val; + unsigned int wave_cnt; + /* + * Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID + * parameters to read out waves in flight. Get doorbell offset if there are + * non-zero waves in flight. + */ + pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe; + queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe; + nv_grbm_select(adev, 1, pipe_idx, queue_slot, 0); + reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, 0, + mmSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot); + wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK; + if (wave_cnt != 0) { + queue_cnt->wave_cnt += wave_cnt; + queue_cnt->doorbell_off = + (RREG32_SOC15(GC, 0, mmCP_HQD_PQ_DOORBELL_CONTROL) & + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >> + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + } +} + +/** + * kgd_gfx_v10_get_cu_occupancy: Reads relevant registers associated with each + * shader engine and aggregates the number of waves that are in flight for the + * process whose pasid is provided as a parameter. The process could have ZERO + * or more queues running and submitting waves to compute units. + * + * @adev: Handle of device from which to get number of waves in flight + * @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset + * for comparison later. + * @max_waves_per_cu: Output parameter updated with maximum number of waves + * possible per Compute Unit + * @inst: xcc's instance number on a multi-XCC setup + * + * Note: It's possible that the device has too many queues (oversubscription) + * in which case a VMID could be remapped to a different PASID. This could lead + * to an inaccurate wave count. Following is a high-level sequence: + * Time T1: vmid = getVmid(); vmid is associated with Pasid P1 + * Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2 + * In the sequence above wave count obtained from time T1 will be incorrectly + * lost or added to total wave count. + * + * The registers that provide the waves in flight are: + * + * SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a + * queue is slotted, OFF if there is no queue. A process could have ZERO or + * more queues slotted and submitting waves to be run on compute units. Even + * when there is a queue it is possible there could be zero wave fronts, this + * can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem + * command + * + * For each bit that is ON from above: + * + * Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the + * number of waves that are in flight for the queue at specified index. The + * index ranges from 0 to 7. + * + * If non-zero waves are in flight, store the corresponding doorbell offset + * of the queue, along with the wave count. + * + * Determine if the queue belongs to the process by comparing the doorbell + * offset against the process's queues. If it matches, aggregate the wave + * count for the process. + * + * Reading registers referenced above involves programming GRBM appropriately + */ +void kgd_gfx_v10_get_cu_occupancy(struct amdgpu_device *adev, + struct kfd_cu_occupancy *cu_occupancy, + int *max_waves_per_cu, uint32_t inst) +{ + int qidx; + int se_idx; + int se_cnt; + int queue_map; + int max_queue_cnt; + DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES); + + lock_spi_csq_mutexes(adev); + nv_grbm_select(adev, 1, 0, 0, 0); + + /* + * Iterate through the shader engines and arrays of the device + * to get number of waves in flight + */ + bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap, + AMDGPU_MAX_QUEUES); + max_queue_cnt = adev->gfx.mec.num_pipe_per_mec * + adev->gfx.mec.num_queue_per_pipe; + se_cnt = adev->gfx.config.max_shader_engines; + for (se_idx = 0; se_idx < se_cnt; se_idx++) { + amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst); + queue_map = RREG32_SOC15(GC, 0, + mmSPI_CSQ_WF_ACTIVE_STATUS); + + for (qidx = 0; qidx < max_queue_cnt; qidx++) { + /* Skip queues that are not associated with + * compute functions + */ + if (!test_bit(qidx, cp_queue_bitmap)) + continue; + + if (!(queue_map & (1 << qidx))) + continue; + + /* Get number of waves in flight and aggregate them */ + get_wave_count(adev, qidx, &cu_occupancy[qidx], inst); + } + } + + amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst); + nv_grbm_select(adev, 0, 0, 0, 0); + unlock_spi_csq_mutexes(adev); + + /* Update the output parameters and return */ + *max_waves_per_cu = adev->gfx.cu_info.simd_per_cu * + adev->gfx.cu_info.max_waves_per_simd; +} + const struct kfd2kgd_calls gfx_v10_kfd2kgd = { .program_sh_mem_settings = kgd_program_sh_mem_settings, .set_pasid_vmid_mapping = kgd_set_pasid_vmid_mapping, @@ -1120,5 +1267,6 @@ const struct kfd2kgd_calls gfx_v10_kfd2kgd = { .program_trap_handler_settings = program_trap_handler_settings, .hqd_get_pq_addr = kgd_gfx_v10_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v10_hqd_reset, + .get_cu_occupancy = kgd_gfx_v10_get_cu_occupancy, .hqd_sdma_get_doorbell = kgd_gfx_v10_hqd_sdma_get_doorbell }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h index df350ea67c30..a91b638c6bcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h @@ -69,3 +69,6 @@ uint64_t kgd_gfx_v10_hqd_reset(struct amdgpu_device *adev, unsigned int utimeout); uint32_t kgd_gfx_v10_hqd_sdma_get_doorbell(struct amdgpu_device *adev, int engine, int queue); +void kgd_gfx_v10_get_cu_occupancy(struct amdgpu_device *adev, + struct kfd_cu_occupancy *cu_occupancy, + int *max_waves_per_cu, uint32_t inst); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c index e31afee42979..50535563efa8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c @@ -683,5 +683,6 @@ const struct kfd2kgd_calls gfx_v10_3_kfd2kgd = { .clear_address_watch = kgd_gfx_v10_clear_address_watch, .hqd_get_pq_addr = kgd_gfx_v10_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v10_hqd_reset, + .get_cu_occupancy = kgd_gfx_v10_get_cu_occupancy, .hqd_sdma_get_doorbell = kgd_gfx_v10_hqd_sdma_get_doorbell }; From 2753320ced79623135c0cc37accbf68fc7cdad28 Mon Sep 17 00:00:00 2001 From: David Belanger Date: Fri, 21 Aug 2026 10:25:26 -0400 Subject: [PATCH 072/114] drm/amdkfd: Disable gfxoff for CU occupancy Disable gfxoff while collecting CU occupancy data. Reported-by: Alex Deucher Reviewed-by: Alex Deucher Signed-off-by: David Belanger Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index dd938788bdd9..226f52626e84 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -318,8 +318,10 @@ static int kfd_get_cu_occupancy(struct attribute *attr, char *buffer) * XCCs in the partition. * For PM4 queues, there is no cooperative dispatch so wave_cnt stay as it is. */ + amdgpu_gfx_off_ctrl(dev->adev, false); dev->kfd2kgd->get_cu_occupancy(dev->adev, cu_occupancy, &max_waves_per_cu, ffs(dev->xcc_mask) - 1); + amdgpu_gfx_off_ctrl(dev->adev, true); for (i = 0; i < AMDGPU_MAX_QUEUES; i++) { if (cu_occupancy[i].wave_cnt != 0 && From b1f9601237d050f5df478464cf51bf1fff29a256 Mon Sep 17 00:00:00 2001 From: Vladimir Marioukhine Date: Wed, 12 Aug 2026 13:19:46 -0400 Subject: [PATCH 073/114] drm/amdkfd: implement restore_mqd callbacks for GFX12/12.1 kfd_mqd_manager_v12.c (GFX 12.0) and kfd_mqd_manager_v12_1.c (GFX 12.1) do not implement restore_mqd callbacks, leaving the function pointers NULL and causing CRIU restore to return -EOPNOTSUPP on GFX12. Implement restore_mqd for both compute and SDMA queues in kfd_mqd_manager_v12.c and kfd_mqd_manager_v12_1.c, modeled after the GFX 11 implementation with the following improvements: - update cp_mqd_base_addr_lo/hi to the newly allocated MQD address, fixing a pre-existing gap shared with v11 where the in-MQD copy still pointed at the old checkpoint-time address after restore - memset the full allocation before memcpy for compute queues to avoid stale data in the GTT sub-allocator tail; SDMA MQDs use sizeof(*m) since they are packed at mqd_size stride in a shared BO checkpoint_mqd registration is deferred to a follow-up patch that also implements get_checkpoint_info, so that checkpoint and restore are enabled together as a complete and testable unit. Note: GFX12.1 restore handles XCC0 only. Multi-XCC CRIU restore is currently unreachable due to a separate validation issue in kfd_criu_restore_queue(). A pr_warn_once() is emitted if a multi-XCC device is encountered. Signed-off-by: Vladimir Marioukhine Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 7 +- .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c | 59 ++++++++++++++++ .../drm/amd/amdkfd/kfd_mqd_manager_v12_1.c | 68 +++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 3ec6a73af22e..0ab2d536b122 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -797,10 +797,12 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; if (qd && !mqd_mgr->restore_mqd) { - pr_debug("restore_mqd not implemented for this GPU\n"); + pr_debug("restore_mqd not implemented for queue type %d\n", + q->properties.type); retval = -EOPNOTSUPP; goto deallocate_vmid; } + if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE) { retval = allocate_hqd(dqm, q); if (retval) @@ -2317,7 +2319,8 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; if (qd && !mqd_mgr->restore_mqd) { - pr_debug("restore_mqd not implemented for this GPU\n"); + pr_debug("restore_mqd not implemented for queue type %d\n", + q->properties.type); retval = -EOPNOTSUPP; goto out_deallocate_doorbell; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c index 7c387fa90076..63f25a60baa5 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c @@ -380,6 +380,63 @@ static int debugfs_show_mqd_sdma(struct seq_file *m, void *data) #endif +static void restore_mqd(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, const void *mqd_src, + const void *ctl_stack_src, const u32 ctl_stack_size) +{ + u64 addr; + struct v12_compute_mqd *m; + + m = (struct v12_compute_mqd *)mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size)); + memcpy(m, mqd_src, sizeof(*m)); + + /* Update MQD base address to the newly allocated location */ + m->cp_mqd_base_addr_lo = lower_32_bits(addr); + m->cp_mqd_base_addr_hi = upper_32_bits(addr); + + m->cp_hqd_pq_doorbell_control &= + ~CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK; + m->cp_hqd_pq_doorbell_control |= + qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control); + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} + +static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, + const u32 ctl_stack_size) +{ + u64 addr; + struct v12_sdma_mqd *m; + + m = (struct v12_sdma_mqd *)mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size)); + memcpy(m, mqd_src, sizeof(*m)); + + m->sdmax_rlcx_doorbell_offset = + qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} + struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type, struct kfd_node *dev) { @@ -407,6 +464,7 @@ struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type, mqd->mqd_size = sizeof(struct v12_compute_mqd); mqd->get_wave_state = get_wave_state; mqd->mqd_stride = kfd_mqd_stride; + mqd->restore_mqd = restore_mqd; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -453,6 +511,7 @@ struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type, mqd->is_occupied = kfd_is_occupied_sdma; mqd->mqd_size = sizeof(struct v12_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; + mqd->restore_mqd = restore_mqd_sdma; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd_sdma; #endif diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index 92ec081842df..60de32cb9ecd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -667,6 +667,72 @@ static int debugfs_show_mqd_sdma(struct seq_file *m, void *data) #endif +static void restore_mqd_v12_1(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, const void *mqd_src, + const void *ctl_stack_src, const u32 ctl_stack_size) +{ + u64 addr; + struct v12_1_compute_mqd *m; + + /* + * GFX12.1 is multi-XCC capable but this restore handles XCC0 only. + * Multi-XCC CRIU restore is currently unreachable because + * kfd_criu_restore_queue() validates against unscaled mqd_size. + */ + if (NUM_XCC(mm->dev->xcc_mask) > 1) + pr_warn_once("GFX12.1 multi-XCC CRIU restore not fully supported\n"); + + m = (struct v12_1_compute_mqd *)mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size) * + NUM_XCC(mm->dev->xcc_mask)); + memcpy(m, mqd_src, sizeof(*m)); + + /* Update MQD base address to the newly allocated location */ + m->cp_mqd_base_addr_lo = lower_32_bits(addr); + m->cp_mqd_base_addr_hi = upper_32_bits(addr); + + m->cp_hqd_pq_doorbell_control &= + ~CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK; + m->cp_hqd_pq_doorbell_control |= + qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control); + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} + +static void restore_mqd_sdma_v12_1(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, + const u32 ctl_stack_size) +{ + u64 addr; + struct v12_sdma_mqd *m; + + m = (struct v12_sdma_mqd *)mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size)); + memcpy(m, mqd_src, sizeof(*m)); + + m->sdmax_rlcx_doorbell_offset = + qp->doorbell_off << SDMA0_SDMA_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + qp->is_active = 0; +} + struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type, struct kfd_node *dev) { @@ -694,6 +760,7 @@ struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type, mqd->mqd_size = sizeof(struct v12_1_compute_mqd); mqd->get_wave_state = get_wave_state_v12_1; mqd->mqd_stride = kfd_mqd_stride; + mqd->restore_mqd = restore_mqd_v12_1; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -740,6 +807,7 @@ struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type, mqd->is_occupied = kfd_is_occupied_sdma; mqd->mqd_size = sizeof(struct v12_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; + mqd->restore_mqd = restore_mqd_sdma_v12_1; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd_sdma; #endif From 088c6a324d9f01e2d89e6cf543c6abe07cf0191d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:44 +0200 Subject: [PATCH 074/114] drm/amdgpu/sdma: Remove unimplemented soft_reset() for SDMA and SI DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are not called from anywhere and don't do anything. Let's delete them. Also remove the srbm_soft_reset field from amdgpu_sdma which is not set anywhere anymore. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 1 - drivers/gpu/drm/amd/amdgpu/cik_sdma.c | 2 -- drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 8 +++----- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 8 -------- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 8 -------- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 8 -------- drivers/gpu/drm/amd/amdgpu/si_dma.c | 7 ------- 7 files changed, 3 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index 659836c2c377..ecb60af86e3b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -110,7 +110,6 @@ struct amdgpu_sdma { int num_inst_per_aid; int num_inst_per_xcc; }; - uint32_t srbm_soft_reset; bool has_page_queue; struct ras_common_if *ras_if; struct amdgpu_sdma_ras *ras; diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index de2323e23566..511c9dd3778d 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -55,8 +55,6 @@ static void cik_sdma_set_irq_funcs(struct amdgpu_device *adev); static void cik_sdma_set_buffer_funcs(struct amdgpu_device *adev); static int cik_sdma_soft_reset(struct amdgpu_ip_block *ip_block); -u32 amdgpu_cik_gpu_check_soft_reset(struct amdgpu_device *adev); - MODULE_FIRMWARE("amdgpu/bonaire_sdma.bin"); MODULE_FIRMWARE("amdgpu/bonaire_sdma1.bin"); MODULE_FIRMWARE("amdgpu/hawaii_sdma.bin"); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 9478dd034aff..63c94fe52b3c 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -1234,13 +1234,11 @@ static int sdma_v3_0_wait_for_idle(struct amdgpu_ip_block *ip_block) static int sdma_v3_0_soft_reset(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; + u32 srbm_soft_reset; u32 tmp; - if (!adev->sdma.srbm_soft_reset) - return 0; - - srbm_soft_reset = adev->sdma.srbm_soft_reset; + srbm_soft_reset = SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK | + SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK; if (srbm_soft_reset) { tmp = RREG32(mmSRBM_SOFT_RESET); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index 9d7d919a5aa1..ded1f8abfb08 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -2051,13 +2051,6 @@ static int sdma_v4_0_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v4_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - /* todo */ - - return 0; -} - static int sdma_v4_0_set_trap_irq_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, unsigned type, @@ -2403,7 +2396,6 @@ const struct amd_ip_funcs sdma_v4_0_ip_funcs = { .suspend = sdma_v4_0_suspend, .resume = sdma_v4_0_resume, .wait_for_idle = sdma_v4_0_wait_for_idle, - .soft_reset = sdma_v4_0_soft_reset, .set_clockgating_state = sdma_v4_0_set_clockgating_state, .set_powergating_state = sdma_v4_0_set_powergating_state, .get_clockgating_state = sdma_v4_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 461f8b220a3e..5212ea6c46a2 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -1651,13 +1651,6 @@ static int sdma_v4_4_2_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v4_4_2_soft_reset(struct amdgpu_ip_block *ip_block) -{ - /* todo */ - - return 0; -} - static bool sdma_v4_4_2_is_queue_selected(struct amdgpu_device *adev, uint32_t instance_id, bool is_page_queue) { uint32_t reg_offset = is_page_queue ? regSDMA_PAGE_CONTEXT_STATUS : regSDMA_GFX_CONTEXT_STATUS; @@ -2109,7 +2102,6 @@ const struct amd_ip_funcs sdma_v4_4_2_ip_funcs = { .suspend = sdma_v4_4_2_suspend, .resume = sdma_v4_4_2_resume, .wait_for_idle = sdma_v4_4_2_wait_for_idle, - .soft_reset = sdma_v4_4_2_soft_reset, .set_clockgating_state = sdma_v4_4_2_set_clockgating_state, .set_powergating_state = sdma_v4_4_2_set_powergating_state, .get_clockgating_state = sdma_v4_4_2_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index 97fee70dc2f6..a6fcef36c501 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -1522,13 +1522,6 @@ static int sdma_v5_0_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v5_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - /* todo */ - - return 0; -} - static int sdma_v5_0_reset_queue(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) @@ -1906,7 +1899,6 @@ static const struct amd_ip_funcs sdma_v5_0_ip_funcs = { .suspend = sdma_v5_0_suspend, .resume = sdma_v5_0_resume, .wait_for_idle = sdma_v5_0_wait_for_idle, - .soft_reset = sdma_v5_0_soft_reset, .set_clockgating_state = sdma_v5_0_set_clockgating_state, .set_powergating_state = sdma_v5_0_set_powergating_state, .get_clockgating_state = sdma_v5_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/si_dma.c b/drivers/gpu/drm/amd/amdgpu/si_dma.c index 3705a963e660..31ab701d45d3 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dma.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dma.c @@ -600,12 +600,6 @@ static int si_dma_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int si_dma_soft_reset(struct amdgpu_ip_block *ip_block) -{ - drm_info(adev_to_drm(ip_block->adev), "si_dma_soft_reset --- not implemented !!!!!!!\n"); - return 0; -} - static int si_dma_set_trap_irq_state(struct amdgpu_device *adev, struct amdgpu_irq_src *src, unsigned type, @@ -732,7 +726,6 @@ static const struct amd_ip_funcs si_dma_ip_funcs = { .suspend = si_dma_suspend, .resume = si_dma_resume, .wait_for_idle = si_dma_wait_for_idle, - .soft_reset = si_dma_soft_reset, .set_clockgating_state = si_dma_set_clockgating_state, .set_powergating_state = si_dma_set_powergating_state, }; From 5992cc68111b43eb56716046402bbfec80c99e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:45 +0200 Subject: [PATCH 075/114] drm/amdgpu/sdma: Remove superfluous rlc_resume and rlc_stop functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every SDMA generation has an rlc_resume and rlc_stop function which are not doing anything. Starting from CIK, the SDMA engine supports additional queues which are called "SDMA compute" or "RLC" queues, and these queues are used by the KFD, and configured using the HWS. There is nothing for the kernel driver to do for these. Let's delete the functions that don't do anything. In case of SDMA v4.0 and v4.4.2 let's move the init_pg() call to the start() function. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/cik_sdma.c | 30 -------------------- drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c | 30 -------------------- drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 30 -------------------- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 32 +-------------------- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 36 +----------------------- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 27 ------------------ drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 27 ------------------ drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 27 ------------------ drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 27 ------------------ 9 files changed, 2 insertions(+), 264 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index 511c9dd3778d..f444207fa550 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -316,18 +316,6 @@ static void cik_sdma_gfx_stop(struct amdgpu_device *adev) } } -/** - * cik_sdma_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues (CIK). - */ -static void cik_sdma_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * cik_ctx_switch_enable - stop the async dma engines context switch * @@ -400,7 +388,6 @@ static void cik_sdma_enable(struct amdgpu_device *adev, bool enable) if (!enable) { cik_sdma_gfx_stop(adev); - cik_sdma_rlc_stop(adev); } for (i = 0; i < adev->sdma.num_instances; i++) { @@ -501,20 +488,6 @@ static int cik_sdma_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * cik_sdma_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them (CIK). - * Returns 0 for success, error for failure. - */ -static int cik_sdma_rlc_resume(struct amdgpu_device *adev) -{ - /* XXX todo */ - return 0; -} - /** * cik_sdma_load_microcode - load the sDMA ME ucode * @@ -577,9 +550,6 @@ static int cik_sdma_start(struct amdgpu_device *adev) /* start the gfx rings and rlc compute queues */ r = cik_sdma_gfx_resume(adev); - if (r) - return r; - r = cik_sdma_rlc_resume(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 657ef6c93c61..7c8acdf73d44 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -349,18 +349,6 @@ static void sdma_v2_4_gfx_stop(struct amdgpu_device *adev) } } -/** - * sdma_v2_4_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues (VI). - */ -static void sdma_v2_4_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v2_4_enable - stop the async dma engines * @@ -376,7 +364,6 @@ static void sdma_v2_4_enable(struct amdgpu_device *adev, bool enable) if (!enable) { sdma_v2_4_gfx_stop(adev); - sdma_v2_4_rlc_stop(adev); } for (i = 0; i < adev->sdma.num_instances; i++) { @@ -477,20 +464,6 @@ static int sdma_v2_4_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * sdma_v2_4_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them (VI). - * Returns 0 for success, error for failure. - */ -static int sdma_v2_4_rlc_resume(struct amdgpu_device *adev) -{ - /* XXX todo */ - return 0; -} - /** * sdma_v2_4_start - setup and start the async dma engines @@ -509,9 +482,6 @@ static int sdma_v2_4_start(struct amdgpu_device *adev) /* start the gfx rings and rlc compute queues */ r = sdma_v2_4_gfx_resume(adev); - if (r) - return r; - r = sdma_v2_4_rlc_resume(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 63c94fe52b3c..e929d62721b3 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -526,18 +526,6 @@ static void sdma_v3_0_gfx_stop(struct amdgpu_device *adev) } } -/** - * sdma_v3_0_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues (VI). - */ -static void sdma_v3_0_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v3_0_ctx_switch_enable - stop the async dma engines context switch * @@ -614,7 +602,6 @@ static void sdma_v3_0_enable(struct amdgpu_device *adev, bool enable) if (!enable) { sdma_v3_0_gfx_stop(adev); - sdma_v3_0_rlc_stop(adev); } for (i = 0; i < adev->sdma.num_instances; i++) { @@ -752,20 +739,6 @@ static int sdma_v3_0_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * sdma_v3_0_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them (VI). - * Returns 0 for success, error for failure. - */ -static int sdma_v3_0_rlc_resume(struct amdgpu_device *adev) -{ - /* XXX todo */ - return 0; -} - /** * sdma_v3_0_start - setup and start the async dma engines * @@ -784,9 +757,6 @@ static int sdma_v3_0_start(struct amdgpu_device *adev) /* start the gfx rings and rlc compute queues */ r = sdma_v3_0_gfx_resume(adev); - if (r) - return r; - r = sdma_v3_0_rlc_resume(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index ded1f8abfb08..bdd140887e10 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -933,18 +933,6 @@ static void sdma_v4_0_gfx_enable(struct amdgpu_device *adev, bool enable) } } -/** - * sdma_v4_0_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues (VEGA10). - */ -static void sdma_v4_0_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v4_0_page_stop - stop the page async dma engines * @@ -1047,7 +1035,6 @@ static void sdma_v4_0_enable(struct amdgpu_device *adev, bool enable) if (!enable) { sdma_v4_0_gfx_enable(adev, enable); - sdma_v4_0_rlc_stop(adev); if (adev->sdma.has_page_queue) sdma_v4_0_page_stop(adev); } @@ -1312,21 +1299,6 @@ static void sdma_v4_0_init_pg(struct amdgpu_device *adev) } } -/** - * sdma_v4_0_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them (VEGA10). - * Returns 0 for success, error for failure. - */ -static int sdma_v4_0_rlc_resume(struct amdgpu_device *adev) -{ - sdma_v4_0_init_pg(adev); - - return 0; -} - /** * sdma_v4_0_load_microcode - load the sDMA ME ucode * @@ -1426,9 +1398,7 @@ static int sdma_v4_0_start(struct amdgpu_device *adev) sdma_v4_0_ctx_switch_enable(adev, true); sdma_v4_0_enable(adev, true); } else { - r = sdma_v4_0_rlc_resume(adev); - if (r) - return r; + sdma_v4_0_init_pg(adev); } for (i = 0; i < adev->sdma.num_instances; i++) { diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 5212ea6c46a2..7e8d528cf422 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -520,20 +520,6 @@ static void sdma_v4_4_2_inst_gfx_stop(struct amdgpu_device *adev, } } -/** - * sdma_v4_4_2_inst_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * @inst_mask: mask of dma engine instances to be disabled - * - * Stop the compute async dma queues. - */ -static void sdma_v4_4_2_inst_rlc_stop(struct amdgpu_device *adev, - uint32_t inst_mask) -{ - /* XXX todo */ -} - /** * sdma_v4_4_2_inst_page_stop - stop the page async dma engines * @@ -632,7 +618,6 @@ static void sdma_v4_4_2_inst_enable(struct amdgpu_device *adev, bool enable, if (!enable) { sdma_v4_4_2_inst_gfx_stop(adev, inst_mask); - sdma_v4_4_2_inst_rlc_stop(adev, inst_mask); if (adev->sdma.has_page_queue) sdma_v4_4_2_inst_page_stop(adev, inst_mask); @@ -888,23 +873,6 @@ static void sdma_v4_4_2_init_pg(struct amdgpu_device *adev) } -/** - * sdma_v4_4_2_inst_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * @inst_mask: mask of dma engine instances to be enabled - * - * Set up the compute DMA queues and enable them. - * Returns 0 for success, error for failure. - */ -static int sdma_v4_4_2_inst_rlc_resume(struct amdgpu_device *adev, - uint32_t inst_mask) -{ - sdma_v4_4_2_init_pg(adev); - - return 0; -} - /** * sdma_v4_4_2_inst_load_microcode - load the sDMA ME ucode * @@ -1019,9 +987,7 @@ static int sdma_v4_4_2_inst_start(struct amdgpu_device *adev, sdma_v4_4_2_inst_ctx_switch_enable(adev, true, inst_mask); sdma_v4_4_2_inst_enable(adev, true, inst_mask); } else { - r = sdma_v4_4_2_inst_rlc_resume(adev, inst_mask); - if (r) - return r; + sdma_v4_4_2_init_pg(adev); } tmp_mask = inst_mask; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index a6fcef36c501..d24659a69382 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -574,18 +574,6 @@ static void sdma_v5_0_gfx_stop(struct amdgpu_device *adev, uint32_t inst_mask) } } -/** - * sdma_v5_0_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues (NAVI10). - */ -static void sdma_v5_0_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v5_0_ctx_switch_enable - stop the async dma engines context switch * @@ -661,7 +649,6 @@ static void sdma_v5_0_enable(struct amdgpu_device *adev, bool enable) inst_mask = GENMASK(adev->sdma.num_instances - 1, 0); if (!enable) { sdma_v5_0_gfx_stop(adev, 1 << inst_mask); - sdma_v5_0_rlc_stop(adev); } if (amdgpu_sriov_vf(adev)) @@ -858,19 +845,6 @@ static int sdma_v5_0_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * sdma_v5_0_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them (NAVI10). - * Returns 0 for success, error for failure. - */ -static int sdma_v5_0_rlc_resume(struct amdgpu_device *adev) -{ - return 0; -} - /** * sdma_v5_0_load_microcode - load the sDMA ME ucode * @@ -951,7 +925,6 @@ static int sdma_v5_0_start(struct amdgpu_device *adev) r = sdma_v5_0_gfx_resume(adev); if (r) return r; - r = sdma_v5_0_rlc_resume(adev); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index 35cdf6c149f8..76e1f9f39525 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -424,18 +424,6 @@ static void sdma_v5_2_gfx_stop(struct amdgpu_device *adev, uint32_t inst_mask) } } -/** - * sdma_v5_2_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues. - */ -static void sdma_v5_2_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v5_2_ctx_switch_enable - stop the async dma engines context switch * @@ -510,7 +498,6 @@ static void sdma_v5_2_enable(struct amdgpu_device *adev, bool enable) inst_mask = GENMASK(adev->sdma.num_instances - 1, 0); if (!enable) { sdma_v5_2_gfx_stop(adev, inst_mask); - sdma_v5_2_rlc_stop(adev); } if (!amdgpu_sriov_vf(adev)) { @@ -705,19 +692,6 @@ static int sdma_v5_2_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * sdma_v5_2_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them. - * Returns 0 for success, error for failure. - */ -static int sdma_v5_2_rlc_resume(struct amdgpu_device *adev) -{ - return 0; -} - /** * sdma_v5_2_load_microcode - load the sDMA ME ucode * @@ -851,7 +825,6 @@ static int sdma_v5_2_start(struct amdgpu_device *adev) r = sdma_v5_2_gfx_resume(adev); if (r) return r; - r = sdma_v5_2_rlc_resume(adev); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index 0b64eb5b32e9..d9e134b2bc48 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -407,18 +407,6 @@ static void sdma_v6_0_gfx_stop(struct amdgpu_device *adev) } } -/** - * sdma_v6_0_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues. - */ -static void sdma_v6_0_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v6_0_ctxempty_int_enable - enable or disable context empty interrupts * @@ -457,7 +445,6 @@ static void sdma_v6_0_enable(struct amdgpu_device *adev, bool enable) if (!enable) { sdma_v6_0_gfx_stop(adev); - sdma_v6_0_rlc_stop(adev); } if (amdgpu_sriov_vf(adev)) @@ -642,19 +629,6 @@ static int sdma_v6_0_gfx_resume(struct amdgpu_device *adev) return 0; } -/** - * sdma_v6_0_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them. - * Returns 0 for success, error for failure. - */ -static int sdma_v6_0_rlc_resume(struct amdgpu_device *adev) -{ - return 0; -} - /** * sdma_v6_0_load_microcode - load the sDMA ME ucode * @@ -832,7 +806,6 @@ static int sdma_v6_0_start(struct amdgpu_device *adev) r = sdma_v6_0_gfx_resume(adev); if (r) return r; - r = sdma_v6_0_rlc_resume(adev); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index c55c941b82cf..f3bec3b2d728 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -409,18 +409,6 @@ static void sdma_v7_0_gfx_stop(struct amdgpu_device *adev) } } -/** - * sdma_v7_0_rlc_stop - stop the compute async dma engines - * - * @adev: amdgpu_device pointer - * - * Stop the compute async dma queues. - */ -static void sdma_v7_0_rlc_stop(struct amdgpu_device *adev) -{ - /* XXX todo */ -} - /** * sdma_v7_0_ctx_switch_enable - stop the async dma engines context switch * @@ -448,7 +436,6 @@ static void sdma_v7_0_enable(struct amdgpu_device *adev, bool enable) if (!enable) { sdma_v7_0_gfx_stop(adev); - sdma_v7_0_rlc_stop(adev); } if (amdgpu_sriov_vf(adev)) @@ -643,19 +630,6 @@ static int sdma_v7_0_gfx_resume(struct amdgpu_device *adev) } -/** - * sdma_v7_0_rlc_resume - setup and start the async dma engines - * - * @adev: amdgpu_device pointer - * - * Set up the compute DMA queues and enable them. - * Returns 0 for success, error for failure. - */ -static int sdma_v7_0_rlc_resume(struct amdgpu_device *adev) -{ - return 0; -} - static void sdma_v12_0_free_ucode_buffer(struct amdgpu_device *adev) { int i; @@ -878,7 +852,6 @@ static int sdma_v7_0_start(struct amdgpu_device *adev) r = sdma_v7_0_gfx_resume(adev); if (r) return r; - r = sdma_v7_0_rlc_resume(adev); return r; } From 0518c66cf841f5f701143ffa72840ad367838490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:46 +0200 Subject: [PATCH 076/114] drm/amdgpu/sdma: Fix executing duplicate commands after recovery on SDMA v4.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDMA v4.4.2 had a custom recovery implementation that tried to keep ring contents and restore the rptr after an SDMA soft reset. Later it was changed to use the more common amdgpu_sdma_reset_engine() implementation. Currently, SDMA v4.4.2 may execute some commands twice after a recovery: 1. From the old ring buffer contents at the restored rptr 2. From the contents restored by amdgpu_sdma_reset_engine() that calls the ring reset helper functions. Fix that by removing the code that restores rptr. This is not needed anymore because amdgpu_sdma_reset_engine() already saves and restores the contents of both the SDMA gfx and paging rings, so there is no need to do anything extra. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c | 2 - drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h | 2 - drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 76 +++++------------------- 3 files changed, 14 insertions(+), 66 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 686c92e96025..132ca5ce3a6d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -347,8 +347,6 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, ring->buf_mask = (ring->ring_size / 4) - 1; ring->ptr_mask = ring->funcs->support_64bit_ptrs ? 0xffffffffffffffff : ring->buf_mask; - /* Initialize cached_rptr to 0 */ - ring->cached_rptr = 0; if (!ring->ring_backup) { ring->ring_backup = kvzalloc(ring->ring_size, GFP_KERNEL); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 6b6ee4083c8d..61e30de1977b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -423,8 +423,6 @@ struct amdgpu_ring { bool is_sw_ring; unsigned int entry_index; - /* store the cached rptr to restore after reset */ - uint64_t cached_rptr; }; #define amdgpu_ring_parse_cs(r, p, job, ib) ((r)->funcs->parse_cs((p), (job), (ib))) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 7e8d528cf422..53c816043eb8 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -660,12 +660,11 @@ static uint32_t sdma_v4_4_2_rb_cntl(struct amdgpu_ring *ring, uint32_t rb_cntl) * * @adev: amdgpu_device pointer * @i: instance to resume - * @restore: used to restore wptr when restart * * Set up the gfx DMA ring buffers and enable them. * Returns 0 for success, error for failure. */ -static void sdma_v4_4_2_gfx_resume(struct amdgpu_device *adev, unsigned int i, bool restore) +static void sdma_v4_4_2_gfx_resume(struct amdgpu_device *adev, unsigned int i) { struct amdgpu_ring *ring = &adev->sdma.instance[i].ring; u32 rb_cntl, ib_cntl, wptr_poll_cntl; @@ -673,7 +672,6 @@ static void sdma_v4_4_2_gfx_resume(struct amdgpu_device *adev, unsigned int i, b u32 doorbell; u32 doorbell_offset; u64 wptr_gpu_addr; - u64 rwptr; wb_offset = (ring->rptr_offs * 4); @@ -693,32 +691,16 @@ static void sdma_v4_4_2_gfx_resume(struct amdgpu_device *adev, unsigned int i, b WREG32_SDMA(i, regSDMA_GFX_RB_BASE, ring->gpu_addr >> 8); WREG32_SDMA(i, regSDMA_GFX_RB_BASE_HI, ring->gpu_addr >> 40); - if (!restore) - ring->wptr = 0; + ring->wptr = 0; /* before programing wptr to a less value, need set minor_ptr_update first */ WREG32_SDMA(i, regSDMA_GFX_MINOR_PTR_UPDATE, 1); - /* For the guilty queue, set RPTR to the current wptr to skip bad commands, - * It is not a guilty queue, restore cache_rptr and continue execution. - */ - if (adev->sdma.instance[i].gfx_guilty) - rwptr = ring->wptr; - else - rwptr = ring->cached_rptr; - /* Initialize the ring buffer's read and write pointers */ - if (restore) { - WREG32_SDMA(i, regSDMA_GFX_RB_RPTR, lower_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_GFX_RB_RPTR_HI, upper_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_GFX_RB_WPTR, lower_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_GFX_RB_WPTR_HI, upper_32_bits(rwptr << 2)); - } else { - WREG32_SDMA(i, regSDMA_GFX_RB_RPTR, 0); - WREG32_SDMA(i, regSDMA_GFX_RB_RPTR_HI, 0); - WREG32_SDMA(i, regSDMA_GFX_RB_WPTR, 0); - WREG32_SDMA(i, regSDMA_GFX_RB_WPTR_HI, 0); - } + WREG32_SDMA(i, regSDMA_GFX_RB_RPTR, 0); + WREG32_SDMA(i, regSDMA_GFX_RB_RPTR_HI, 0); + WREG32_SDMA(i, regSDMA_GFX_RB_WPTR, 0); + WREG32_SDMA(i, regSDMA_GFX_RB_WPTR_HI, 0); doorbell = RREG32_SDMA(i, regSDMA_GFX_DOORBELL); doorbell_offset = RREG32_SDMA(i, regSDMA_GFX_DOORBELL_OFFSET); @@ -771,7 +753,7 @@ static void sdma_v4_4_2_gfx_resume(struct amdgpu_device *adev, unsigned int i, b * Set up the page DMA ring buffers and enable them. * Returns 0 for success, error for failure. */ -static void sdma_v4_4_2_page_resume(struct amdgpu_device *adev, unsigned int i, bool restore) +static void sdma_v4_4_2_page_resume(struct amdgpu_device *adev, unsigned int i) { struct amdgpu_ring *ring = &adev->sdma.instance[i].page; u32 rb_cntl, ib_cntl, wptr_poll_cntl; @@ -779,7 +761,6 @@ static void sdma_v4_4_2_page_resume(struct amdgpu_device *adev, unsigned int i, u32 doorbell; u32 doorbell_offset; u64 wptr_gpu_addr; - u64 rwptr; wb_offset = (ring->rptr_offs * 4); @@ -787,26 +768,11 @@ static void sdma_v4_4_2_page_resume(struct amdgpu_device *adev, unsigned int i, rb_cntl = sdma_v4_4_2_rb_cntl(ring, rb_cntl); WREG32_SDMA(i, regSDMA_PAGE_RB_CNTL, rb_cntl); - /* For the guilty queue, set RPTR to the current wptr to skip bad commands, - * It is not a guilty queue, restore cache_rptr and continue execution. - */ - if (adev->sdma.instance[i].page_guilty) - rwptr = ring->wptr; - else - rwptr = ring->cached_rptr; - /* Initialize the ring buffer's read and write pointers */ - if (restore) { - WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR, lower_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR_HI, upper_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR, lower_32_bits(rwptr << 2)); - WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR_HI, upper_32_bits(rwptr << 2)); - } else { - WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR, 0); - WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR_HI, 0); - WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR, 0); - WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR_HI, 0); - } + WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR, 0); + WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR_HI, 0); + WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR, 0); + WREG32_SDMA(i, regSDMA_PAGE_RB_WPTR_HI, 0); /* set the wb address whether it's enabled or not */ WREG32_SDMA(i, regSDMA_PAGE_RB_RPTR_ADDR_HI, @@ -820,8 +786,7 @@ static void sdma_v4_4_2_page_resume(struct amdgpu_device *adev, unsigned int i, WREG32_SDMA(i, regSDMA_PAGE_RB_BASE, ring->gpu_addr >> 8); WREG32_SDMA(i, regSDMA_PAGE_RB_BASE_HI, ring->gpu_addr >> 40); - if (!restore) - ring->wptr = 0; + ring->wptr = 0; /* before programing wptr to a less value, need set minor_ptr_update first */ WREG32_SDMA(i, regSDMA_PAGE_MINOR_PTR_UPDATE, 1); @@ -959,9 +924,9 @@ static int sdma_v4_4_2_inst_start(struct amdgpu_device *adev, uint32_t temp; WREG32_SDMA(i, regSDMA_SEM_WAIT_FAIL_TIMER_CNTL, 0); - sdma_v4_4_2_gfx_resume(adev, i, restore); + sdma_v4_4_2_gfx_resume(adev, i); if (adev->sdma.has_page_queue) - sdma_v4_4_2_page_resume(adev, i, restore); + sdma_v4_4_2_page_resume(adev, i); /* set utc l1 enable flag always to 1 */ temp = RREG32_SDMA(i, regSDMA_CNTL); @@ -1645,7 +1610,6 @@ static int sdma_v4_4_2_stop_queue(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; u32 instance_id = ring->me; u32 inst_mask; - uint64_t rptr; if (amdgpu_sriov_vf(adev)) return -EINVAL; @@ -1657,18 +1621,6 @@ static int sdma_v4_4_2_stop_queue(struct amdgpu_ring *ring) adev->sdma.instance[instance_id].page_guilty = sdma_v4_4_2_is_queue_selected(adev, instance_id, true); - /* Cache the rptr before reset, after the reset, - * all of the registers will be reset to 0 - */ - rptr = amdgpu_ring_get_rptr(ring); - ring->cached_rptr = rptr; - /* Cache the rptr for the page queue if it exists */ - if (adev->sdma.has_page_queue) { - struct amdgpu_ring *page_ring = &adev->sdma.instance[instance_id].page; - rptr = amdgpu_ring_get_rptr(page_ring); - page_ring->cached_rptr = rptr; - } - /* stop queue */ inst_mask = 1 << ring->me; sdma_v4_4_2_inst_gfx_stop(adev, inst_mask); From 3eb5587e133849a47554949190200107368358ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:47 +0200 Subject: [PATCH 077/114] drm/amdgpu/sdma: Remove unnecessary guilty tracking of SDMA queues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_sdma_reset_engine() saves and restores contents of both the SDMA gfx queue and the paging queue, so it's not necessary to track which queue was guilty anymore. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 3 --- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 20 -------------------- 2 files changed, 23 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index ecb60af86e3b..fc60c9f33188 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -81,9 +81,6 @@ struct amdgpu_sdma_instance { uint64_t sdma_fw_gpu_addr; uint32_t *sdma_fw_ptr; struct mutex engine_reset_mutex; - /* track guilty state of GFX and PAGE queues */ - bool gfx_guilty; - bool page_guilty; const struct amdgpu_sdma_funcs *funcs; }; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 53c816043eb8..0ca774959401 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -1409,9 +1409,6 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { mutex_init(&adev->sdma.instance[i].engine_reset_mutex); - /* Initialize guilty flags for GFX and PAGE queues */ - adev->sdma.instance[i].gfx_guilty = false; - adev->sdma.instance[i].page_guilty = false; adev->sdma.instance[i].funcs = &sdma_v4_4_2_sdma_funcs; ring = &adev->sdma.instance[i].ring; @@ -1582,15 +1579,6 @@ static int sdma_v4_4_2_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static bool sdma_v4_4_2_is_queue_selected(struct amdgpu_device *adev, uint32_t instance_id, bool is_page_queue) -{ - uint32_t reg_offset = is_page_queue ? regSDMA_PAGE_CONTEXT_STATUS : regSDMA_GFX_CONTEXT_STATUS; - uint32_t context_status = RREG32(sdma_v4_4_2_get_reg_offset(adev, instance_id, reg_offset)); - - /* Check if the SELECTED bit is set */ - return (context_status & SDMA_GFX_CONTEXT_STATUS__SELECTED_MASK) != 0; -} - static int sdma_v4_4_2_reset_queue(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) @@ -1608,19 +1596,11 @@ static int sdma_v4_4_2_reset_queue(struct amdgpu_ring *ring, static int sdma_v4_4_2_stop_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - u32 instance_id = ring->me; u32 inst_mask; if (amdgpu_sriov_vf(adev)) return -EINVAL; - /* Check if this queue is the guilty one */ - adev->sdma.instance[instance_id].gfx_guilty = - sdma_v4_4_2_is_queue_selected(adev, instance_id, false); - if (adev->sdma.has_page_queue) - adev->sdma.instance[instance_id].page_guilty = - sdma_v4_4_2_is_queue_selected(adev, instance_id, true); - /* stop queue */ inst_mask = 1 << ring->me; sdma_v4_4_2_inst_gfx_stop(adev, inst_mask); From de73eb26f90d6fcab431928fa5f68448ddbf697e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:48 +0200 Subject: [PATCH 078/114] drm/amdgpu: Add amdgpu_ring_clear_ring_and_ptrs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new helper function which clears the contents of a ring and the relevant pointers such as rptr/wptr. Suggested-by: Lijo Lazar Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 61e30de1977b..49df2c4745a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -488,6 +488,25 @@ static inline void amdgpu_ring_clear_ring(struct amdgpu_ring *ring) memset32(ring->ring, ring->funcs->nop, ring->buf_mask + 1); } +static inline void amdgpu_ring_clear_ring_and_ptrs(struct amdgpu_ring *ring) +{ + /* Clear the contents of the ring. */ + amdgpu_ring_clear_ring(ring); + + /* Clear the ring pointers on the CPU. */ + ring->wptr = 0; + + /* + * Clear the ring pointers allocated in writeback. + * + * Note: we always allocate 64 bits for these pointers, + * but older HW generations only use the lower 32 bits. + * Use 64-bit atomics for simplicity. + */ + atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0); + atomic64_set((atomic64_t *)ring->rptr_cpu_addr, 0); +} + static inline void amdgpu_ring_write(struct amdgpu_ring *ring, uint32_t v) { ring->ring[ring->wptr++ & ring->buf_mask] = v; From 973a50908bd635d49b7fcd58e377b77ed0b16bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:49 +0200 Subject: [PATCH 079/114] drm/amdgpu/sdma: Clear SDMA rings after reset before starting them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ring contains commands that were emitted before the reset. These need to be cleared to make sure the HW doesn't execute them, because they are garbage at this point. Note that the ring reset helpers will re-emit the commands that are necessary after the reset. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index fbac732f3e01..3495b7553950 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -593,6 +593,11 @@ int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, goto exit; } + amdgpu_ring_clear_ring_and_ptrs(gfx_ring); + + if (adev->sdma.has_page_queue) + amdgpu_ring_clear_ring_and_ptrs(page_ring); + if (sdma_instance->funcs->start_kernel_queue) { sdma_instance->funcs->start_kernel_queue(gfx_ring); if (adev->sdma.has_page_queue) From 13e47c31e53d615068b7bd0087360c48a05c2cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:50 +0200 Subject: [PATCH 080/114] drm/amdgpu/sdma: Move SDMA v5.x queue reset to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code was exactly the same between SDMA v5.0 and v5.2 furthermore the exact same implementation can be shared between all SDMA versions that don't use MES. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 37 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 4 +++ drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 25 +--------------- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 25 +--------------- 4 files changed, 43 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 3495b7553950..71a7a70a80c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -628,3 +628,40 @@ unlock: return ret; } + +/** + * amdgpu_sdma_reset_queue_legacy() - Reset legacy SDMA queue after timeout (without MES) + * + * @ring: Pointer to the ring of the SDMA queue + * @vmid: VMID of the timed out job + * @timedout_fence: Fence of the timed out job + * + * Common implementation for resetting SDMA queues without MES (legacy). + * This relies on the proper amdgpu_sdma_funcs to be set up + * for the given ring. + * + * Applicable to SDMA versions that don't rely on the MES yet, + * that is all versions up to SDMA v5.x and older. + */ +int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence) +{ + struct amdgpu_device *adev = ring->adev; + int r; + + if (ring->me >= adev->sdma.num_instances) { + dev_err(adev->dev, "sdma instance not found\n"); + return -EINVAL; + } + + amdgpu_ring_reset_helper_begin(ring, timedout_fence); + + amdgpu_amdkfd_suspend(adev, true); + r = amdgpu_sdma_reset_engine(adev, ring->me, true); + amdgpu_amdkfd_resume(adev, true); + if (r) + return r; + + return amdgpu_ring_reset_helper_end(ring, timedout_fence); +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index fc60c9f33188..6d141479bb2a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -159,6 +159,10 @@ struct amdgpu_buffer_funcs { int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, bool caller_handles_kernel_queues); +int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence); + #define amdgpu_emit_copy_buffer(adev, ib, s, d, b, t) (adev)->mman.buffer_funcs->emit_copy_buffer((ib), (s), (d), (b), (t)) #define amdgpu_emit_fill_buffer(adev, ib, s, d, b) (adev)->mman.buffer_funcs->emit_fill_buffer((ib), (s), (d), (b)) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index d24659a69382..a4d2a107c74f 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -1495,29 +1495,6 @@ static int sdma_v5_0_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v5_0_reset_queue(struct amdgpu_ring *ring, - unsigned int vmid, - struct amdgpu_fence *timedout_fence) -{ - struct amdgpu_device *adev = ring->adev; - int r; - - if (ring->me >= adev->sdma.num_instances) { - dev_err(adev->dev, "sdma instance not found\n"); - return -EINVAL; - } - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - amdgpu_amdkfd_suspend(adev, true); - r = amdgpu_sdma_reset_engine(adev, ring->me, true); - amdgpu_amdkfd_resume(adev, true); - if (r) - return r; - - return amdgpu_ring_reset_helper_end(ring, timedout_fence); -} - static int sdma_v5_0_stop_queue(struct amdgpu_ring *ring) { u32 f32_cntl, freeze, cntl, stat1_reg; @@ -1913,7 +1890,7 @@ static const struct amdgpu_ring_funcs sdma_v5_0_ring_funcs = { .emit_reg_write_reg_wait = sdma_v5_0_ring_emit_reg_write_reg_wait, .init_cond_exec = sdma_v5_0_ring_init_cond_exec, .preempt_ib = sdma_v5_0_ring_preempt_ib, - .reset = sdma_v5_0_reset_queue, + .reset = amdgpu_sdma_reset_queue_legacy, }; static void sdma_v5_0_set_ring_funcs(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index 76e1f9f39525..e7613e27e774 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -1414,29 +1414,6 @@ static int sdma_v5_2_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v5_2_reset_queue(struct amdgpu_ring *ring, - unsigned int vmid, - struct amdgpu_fence *timedout_fence) -{ - struct amdgpu_device *adev = ring->adev; - int r; - - if (ring->me >= adev->sdma.num_instances) { - dev_err(adev->dev, "sdma instance not found\n"); - return -EINVAL; - } - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - amdgpu_amdkfd_suspend(adev, true); - r = amdgpu_sdma_reset_engine(adev, ring->me, true); - amdgpu_amdkfd_resume(adev, true); - if (r) - return r; - - return amdgpu_ring_reset_helper_end(ring, timedout_fence); -} - static int sdma_v5_2_stop_queue(struct amdgpu_ring *ring) { u32 f32_cntl, freeze, cntl, stat1_reg; @@ -1929,7 +1906,7 @@ static const struct amdgpu_ring_funcs sdma_v5_2_ring_funcs = { .emit_reg_write_reg_wait = sdma_v5_2_ring_emit_reg_write_reg_wait, .init_cond_exec = sdma_v5_2_ring_init_cond_exec, .preempt_ib = sdma_v5_2_ring_preempt_ib, - .reset = sdma_v5_2_reset_queue, + .reset = amdgpu_sdma_reset_queue_legacy, }; static void sdma_v5_2_set_ring_funcs(struct amdgpu_device *adev) From 5319f554f856d9085c20907e8bde12478a3b230b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:51 +0200 Subject: [PATCH 081/114] drm/amdgpu/sdma: Always handle kernel queues in amdgpu_sdma_reset_engine() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the caller_handles_kernel_queues argument from the amdgpu_sdma_reset_engine() function and make it always handle kernel queues. Now the SDMA recovery sequence is more consistent between callers for the KFD as follows. Before recovery: first the KFD is suspended, then the SDMA queue contents are backed up. After recovery: first the SDMA queue contents are restored, then the KFD is resumed. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 68 ++++++++++--------- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 3 +- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 2 +- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 2 +- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 71a7a70a80c4..0286e3dd958e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -542,16 +542,14 @@ static int amdgpu_sdma_soft_reset(struct amdgpu_device *adev, u32 instance_id) } /** - * amdgpu_sdma_reset_engine - Reset a specific SDMA engine + * amdgpu_sdma_reset_engine() - Reset a specific SDMA engine instance. + * * @adev: Pointer to the AMDGPU device * @instance_id: Logical ID of the SDMA engine instance to reset - * @caller_handles_kernel_queues: Skip kernel queue processing. Caller - * will handle it. * * Returns: 0 on success, or a negative error code on failure. */ -int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, - bool caller_handles_kernel_queues) +int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id) { struct amdgpu_sdma_instance *sdma_instance = &adev->sdma.instance[instance_id]; struct amdgpu_ring *gfx_ring = &sdma_instance->ring; @@ -564,20 +562,23 @@ int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, mutex_lock(&sdma_instance->engine_reset_mutex); - if (!caller_handles_kernel_queues) { - /* Stop the scheduler's work queue for the GFX and page rings if they are running. - * This ensures that no new tasks are submitted to the queues while - * the reset is in progress. - */ + /* + * Stop the scheduler's work queue for the GFX and page rings if they are running. + * This ensures that no new tasks are submitted to the queues while + * the reset is in progress. + */ + if (amdgpu_ring_sched_ready(gfx_ring) && !drm_sched_is_stopped(&gfx_ring->sched)) drm_sched_wqueue_stop(&gfx_ring->sched); - gfx_fence = amdgpu_ring_find_guilty_fence(gfx_ring); - amdgpu_ring_reset_helper_begin(gfx_ring, gfx_fence); - if (adev->sdma.has_page_queue) { + gfx_fence = amdgpu_ring_find_guilty_fence(gfx_ring); + amdgpu_ring_reset_helper_begin(gfx_ring, gfx_fence); + + if (adev->sdma.has_page_queue) { + if (amdgpu_ring_sched_ready(page_ring) && !drm_sched_is_stopped(&page_ring->sched)) drm_sched_wqueue_stop(&page_ring->sched); - page_fence = amdgpu_ring_find_guilty_fence(page_ring); - amdgpu_ring_reset_helper_begin(page_ring, page_fence); - } + + page_fence = amdgpu_ring_find_guilty_fence(page_ring); + amdgpu_ring_reset_helper_begin(page_ring, page_fence); } if (sdma_instance->funcs->stop_kernel_queue) { @@ -605,22 +606,25 @@ int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, } exit: - if (!caller_handles_kernel_queues) { - /* Restart the scheduler's work queue for the GFX and page rings - * if they were stopped by this function. This allows new tasks - * to be submitted to the queues after the reset is complete. - */ - if (!ret) { - ret = amdgpu_ring_reset_helper_end(gfx_ring, gfx_fence); + /* Restart the scheduler's work queue for the GFX and page rings + * if they were stopped by this function. This allows new tasks + * to be submitted to the queues after the reset is complete. + */ + if (!ret) { + ret = amdgpu_ring_reset_helper_end(gfx_ring, gfx_fence); + if (ret) + goto unlock; + + if (amdgpu_ring_sched_ready(gfx_ring)) + drm_sched_wqueue_start(&gfx_ring->sched); + + if (adev->sdma.has_page_queue) { + ret = amdgpu_ring_reset_helper_end(page_ring, page_fence); if (ret) goto unlock; - drm_sched_wqueue_start(&gfx_ring->sched); - if (adev->sdma.has_page_queue) { - ret = amdgpu_ring_reset_helper_end(page_ring, page_fence); - if (ret) - goto unlock; + + if (amdgpu_ring_sched_ready(page_ring)) drm_sched_wqueue_start(&page_ring->sched); - } } } unlock: @@ -655,13 +659,11 @@ int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, return -EINVAL; } - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - amdgpu_amdkfd_suspend(adev, true); - r = amdgpu_sdma_reset_engine(adev, ring->me, true); + r = amdgpu_sdma_reset_engine(adev, ring->me); amdgpu_amdkfd_resume(adev, true); if (r) return r; - return amdgpu_ring_reset_helper_end(ring, timedout_fence); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index 6d141479bb2a..42874b1303ba 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -156,8 +156,7 @@ struct amdgpu_buffer_funcs { uint32_t byte_count); }; -int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, - bool caller_handles_kernel_queues); +int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id); int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, unsigned int vmid, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 0ca774959401..4a1e941cfe2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -1588,7 +1588,7 @@ static int sdma_v4_4_2_reset_queue(struct amdgpu_ring *ring, int r; amdgpu_amdkfd_suspend(adev, true); - r = amdgpu_sdma_reset_engine(adev, id, false); + r = amdgpu_sdma_reset_engine(adev, id); amdgpu_amdkfd_resume(adev, true); return r; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 0ab2d536b122..9da6dfe08600 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2664,7 +2664,7 @@ static int reset_hung_queues_sdma(struct device_queue_manager *dqm) continue; /* Reset engine and check. */ - if (amdgpu_sdma_reset_engine(dqm->dev->adev, i, false) || + if (amdgpu_sdma_reset_engine(dqm->dev->adev, i) || dqm->dev->kfd2kgd->hqd_sdma_get_doorbell(dqm->dev->adev, i, j) || !set_sdma_queue_as_reset(dqm, doorbell_off)) { r = -ENOTRECOVERABLE; From 64437710661b52a61ab998408634061bf36594dc Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 11 Sep 2026 15:11:24 -0400 Subject: [PATCH 082/114] MAINTAINERS: update git tree for radeon, amdgpu, amdkfd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Point to the new repo on gitlab. Cc: christian.koenig@amd.com Cc: felix.kuehling@amd.com Acked-by: Christian König Signed-off-by: Alex Deucher --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6abef8f1a59c..fbd0f999d425 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1218,7 +1218,7 @@ AMD KFD M: Felix Kuehling L: amd-gfx@lists.freedesktop.org S: Supported -T: git https://gitlab.freedesktop.org/agd5f/linux.git +T: git https://gitlab.freedesktop.org/drm/amdgpu/kernel.git F: drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd*.[ch] F: drivers/gpu/drm/amd/amdkfd/ F: drivers/gpu/drm/amd/include/cik_structs.h @@ -22705,7 +22705,7 @@ L: amd-gfx@lists.freedesktop.org S: Supported B: https://gitlab.freedesktop.org/drm/amd/-/issues C: irc://irc.oftc.net/radeon -T: git https://gitlab.freedesktop.org/agd5f/linux.git +T: git https://gitlab.freedesktop.org/drm/amdgpu/kernel.git F: Documentation/gpu/amdgpu/ F: drivers/gpu/drm/amd/ F: drivers/gpu/drm/ci/xfails/amd* From 062ff15e30a48d14fb7d7558eba84f8dc97197f0 Mon Sep 17 00:00:00 2001 From: Mike Lothian Date: Sat, 12 Sep 2026 00:29:08 +0100 Subject: [PATCH 083/114] drm/amdgpu: hold a runtime PM reference for P2P dma-buf attachments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_dma_buf_map() adds VRAM to the allowed domains for a peer2peer attachment. GTT is only a fallback placement when VRAM is preferred, so ttm_bo_validate() migrates the buffer from GTT into VRAM. While the exporting device is runtime suspended its SDMA rings are down and the move fails: amdgpu: Move buffer fallback to memcpy unavailable An importer on a second GPU reaches this holding no runtime PM reference on the exporter, e.g. a compositor on the APU submitting a frame that references a buffer exported by an idle dGPU: amdgpu_cs_ioctl -> amdgpu_cs_parser_bos -> amdgpu_cs_bo_validate -> ttm_bo_validate -> amdgpu_bo_move -> dma_buf_map_attachment -> amdgpu_dma_buf_map -> ttm_bo_validate -> amdgpu_bo_move Pinning a dma-buf into VRAM has the same requirement, which commit 030631e97b20 ("drm/amdgpu: revert "take runtime pm reference when we attach a buffer" v2") called out as the one case that would need the reference back. Take it in attach and drop it in detach. pm_runtime_get_if_active() never resumes the device, so it cannot deadlock against the reservation taken during resume, which is why the old pm_runtime_get_sync() had to go. If the device is not active, clear peer2peer instead: the buffer then stays in GTT, which remains accessible while the GPU is powered down. If runtime PM is disabled, take a plain reference so the put in detach stays balanced. Fixes: 030631e97b20 ("drm/amdgpu: revert "take runtime pm reference when we attach a buffer" v2") Suggested-by: Christian König Reviewed-by: Christian König Signed-off-by: Mike Lothian Assisted-by: Claude:Opus-5 [Claude Code] Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c | 43 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index b4fbd8a407f1..4031ebe075d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -43,6 +43,7 @@ #include #include #include +#include static const struct dma_buf_attach_ops amdgpu_dma_buf_attach_ops; @@ -100,15 +101,54 @@ static int amdgpu_dma_buf_attach(struct dma_buf *dmabuf, pci_p2pdma_distance(adev->pdev, attach->dev, false) < 0) attach->peer2peer = false; + /* + * Only allow P2P while the exporter is active, and keep it active + * until detach. With runtime PM disabled take a plain reference so + * the put in detach stays balanced. + */ + if (attach->peer2peer) { + struct device *dev = adev_to_drm(adev)->dev; + int ret = pm_runtime_get_if_active(dev); + + if (!ret) + attach->peer2peer = false; + else if (ret < 0) + pm_runtime_get_noresume(dev); + } + r = dma_resv_lock(bo->tbo.base.resv, NULL); if (r) - return r; + goto err_pm_put; amdgpu_vm_bo_update_shared(bo); dma_resv_unlock(bo->tbo.base.resv); return 0; + +err_pm_put: + if (attach->peer2peer) + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); + return r; +} + +/** + * amdgpu_dma_buf_detach - &dma_buf_ops.detach implementation + * + * @dmabuf: DMA-buf where we remove the attachment from + * @attach: the attachment to remove + * + * Drop the runtime PM reference taken in amdgpu_dma_buf_attach(). + */ +static void amdgpu_dma_buf_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct drm_gem_object *obj = dmabuf->priv; + struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + + if (attach->peer2peer) + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); } /** @@ -350,6 +390,7 @@ static void amdgpu_dma_buf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map const struct dma_buf_ops amdgpu_dmabuf_ops = { .attach = amdgpu_dma_buf_attach, + .detach = amdgpu_dma_buf_detach, .pin = amdgpu_dma_buf_pin, .unpin = amdgpu_dma_buf_unpin, .map_dma_buf = amdgpu_dma_buf_map, From 9355ae92f2c00514d0c3d12d20843e84bab7ee9d Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Tue, 18 Aug 2026 15:07:19 -0400 Subject: [PATCH 084/114] drm/amdgpu: Fix missed connection cleanup on HELLO reset for UALink When a HELLO arrived on an already ESTABLISHED connection, amdgpu_ualink_process_hello_msg() set the state to PENDING before calling amdgpu_ualink_handle_connection_reset(). That function only runs the imp/exp XA cleanup when it observes the state as ESTABLISHED, so the premature change skipped the cleanup and left stale NPA mappings and pinned BOs from the previous connection. Leave the state as ESTABLISHED and let amdgpu_ualink_handle_connection_reset() perform the transition, so the cleanup runs. Signed-off-by: Mukul Joshi Reviewed-by: Harish Kasiviswanathan Reviewed-by : Philip Yang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c index ab564202f550..f4a4432c2c5d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c @@ -2270,11 +2270,9 @@ static void amdgpu_ualink_process_hello_msg(struct amdgpu_device *adev, /* otherwise, leave it IN_PROGRESS to signal the completion below */ mutex_unlock(&conn_state->lock); } else { - /* Set the connection state back to In Progress and revoke - * all exports and release all imports corresponding to the - * sender GPU. + /* Leave state ESTABLISHED so handle_connection_reset() + * performs the transition and imp/exp XA cleanup. */ - conn_state->state = AMDGPU_UALINK_CONN_PENDING; generation_count = conn_state->generation_count; mutex_unlock(&conn_state->lock); From 1b7f848c1d9ddf639e7f2c7163fae594909fc04f Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 13 Sep 2026 21:20:02 -0500 Subject: [PATCH 085/114] drm/radeon: restore eDP panel power sequencer on Apple DCE3.2 iMacs The Apple iMac10,1 and iMac11,2 (RV730/DCE3.2) drive their internal eDP panel through an LVTMA power sequencer whose enable bit (PWRSEQ_EN in LVTMA_PWRSEQ_CNTL) is programmed exactly once, by EFI at cold boot. Apple's minimal ATOM tables never touch it afterwards, so any GPU re-initialisation - S3 resume or a GPU-lockup reset - clears PWRSEQ_EN and the panel comes back dark while the rest of the GPU resumes fine. The driver never programs this register itself; on every other radeon board the platform firmware maintains it across a re-POST, which is why this only bites the Apple machines. Re-assert PWRSEQ_EN in the eDP DPMS-on path when it is found cleared; the hardware then runs the DIGON->DE->VARY_BL->BLON power-on sequence on its own. Both failure paths pass through here, as radeon_gpu_reset() restores the modeset state via radeon_resume_kms(), so a single site covers resume and reset. The write is gated to Apple parts and to exactly DCE3.2 (ASIC_IS_DCE32 is >= RV730, so DCE4+ must be excluded - Apple also shipped DCE5, e.g. the 2011 MacBook Pro, where the register lives at a different offset). It is skipped when the sequencer is already enabled, so lit panels, DCE4+ boards and non-Apple boards are untouched. DCE3.2 places the register 0x18 above the documented R600 offset. Only iMac11,2 is verified on hardware; iMac10,1 shares the same RV730 design and Apple EFI behaviour. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5719 Acked-by: Alex Deucher Link: https://patch.msgid.link/20260914022002.2005150-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/atombios_encoders.c | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 8b3f8303a967..5b9c72bf4099 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1646,6 +1646,38 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode) } } +/* + * Apple's EFI enables the internal eDP panel power sequencer + * (LVTMA_PWRSEQ_CNTL) exactly once, at cold boot, and its minimal ATOM tables + * never touch it afterwards. Any GPU re-initialisation - S3 resume or a GPU + * reset - clears PWRSEQ_EN, so the panel comes back dark on the DCE3.2 iMacs + * (iMac10,1 / iMac11,2, RV730). Re-assert PWRSEQ_EN when we find it cleared; + * the hardware then runs the DIGON->DE->VARY_BL->BLON power-on sequence itself. + * DCE3.2 places the register 0x18 above the R600 offset. + * + * This is restricted to Apple DCE3.2 parts: the register lives at a different + * offset on DCE4+ (which Apple also shipped, e.g. the 2011 MacBook Pro), and + * non-Apple boards program the sequencer from their own firmware. The + * read-modify-write only fires when the sequencer is actually disabled, so a + * lit panel is never disturbed. + * + * https://gitlab.freedesktop.org/drm/amd/-/issues/5719 + */ +static void radeon_atom_apple_edp_pwrseq_restore(struct radeon_device *rdev) +{ + u32 pwrseq_cntl = R600_LVTMA_PWRSEQ_CNTL + 0x18; + + if (rdev->pdev->subsystem_vendor != PCI_VENDOR_ID_APPLE) + return; + if (!ASIC_IS_DCE32(rdev) || ASIC_IS_DCE4(rdev)) + return; + + if (!(RREG32(pwrseq_cntl) & AVIVO_LVTMA_PWRSEQ_EN)) + WREG32(pwrseq_cntl, AVIVO_LVTMA_PWRSEQ_EN | + AVIVO_LVTMA_SYNCEN_OVRD | + AVIVO_LVTMA_DIGON); +} + static void radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) { @@ -1711,6 +1743,10 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); } if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + /* restore the panel power sequencer on Apple eDP iMacs + * that lose it across resume/reset (see helper above) + */ + radeon_atom_apple_edp_pwrseq_restore(rdev); if (rdev->mode_info.bl_encoder) atombios_set_backlight_level(radeon_encoder, dig->backlight_level); else From 5e6b21bce9d939b564e1f1c88a429cfa1fe66d3a Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 9 Sep 2026 18:23:14 -0400 Subject: [PATCH 086/114] drm/amdgpu: reserve eviction-fence slot at WPTR caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause analysis showed that the previous workaround was fixing the symptom in the wrong layer. Problem: kernel BUG at drivers/dma-buf/dma-resv.c:319! BUG_ON(fobj->num_fences >= fobj->max_fences) Workqueue: events amdgpu_userq_restore_worker - amdgpu_evf_mgr_attach_fence() had been temporarily taught to reserve/add fence bookkeeping internally as a workaround. - This hid the real contract violation and made reservation policy implicit inside a helper. Review clarification: - The helper-side dma_resv_add_fence-related workaround was only an intended proposal sent for review discussion, not the final direction. - Final fix keeps fence-slot reservation explicit in callers and leaves the helper free of policy decisions. Findings from investigation: - drm_exec_prepare_obj() both locks and reserves fence slots. - drm_exec_lock_obj() only locks and does not reserve slots. - Existing attach_fence callers in the GEM/userq restore flows already reserve TTM_NUM_MOVE_FENCES + 1 before adding the bookkeeping eviction fence. - The WPTR mapping path in mes_userq_create_wptr_mapping() used drm_exec_lock_obj() and then called amdgpu_evf_mgr_attach_fence(), so this specific path could add a fence without pre-reserved space. Fix: - Switch the WPTR mapping path to drm_exec_prepare_obj(..., TTM_NUM_MOVE_FENCES + 1), restoring the caller-side reservation contract. - Remove the helper-side reservation workaround from amdgpu_evf_mgr_attach_fence(). Why this is the right layer: - Reservation policy stays explicit at call sites that define how many fences may be added in that execution context. - The helper only performs validate + add_fence when called with a properly prepared reservation object, matching the existing drm_exec model. Fixes: 7264bc10c7c6 (drm/amdgpu: dont pin wptr bo instead use eviction fence) Cc: Sunil Khatri Cc: Prike Liang Cc: Christian König Cc: Alex Deucher Signed-off-by: Vitaly Prosyak Reviewed-by: Prike Liang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 68beccee9fc0..fe49aa4dc5ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -60,7 +60,8 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, } obj = wptr_mapping->bo_va->base.bo; - ret = drm_exec_lock_obj(&exec, &obj->tbo.base); + ret = drm_exec_prepare_obj(&exec, &obj->tbo.base, + TTM_NUM_MOVE_FENCES + 1); drm_exec_retry_on_contention(&exec); if (unlikely(ret)) goto fail_lock; From a2a27745350c7f2d142b3248e12fd3ba182f8766 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 9 Sep 2026 19:23:55 -0400 Subject: [PATCH 087/114] drm/amdgpu: reserve root PD fence slots for userq eviction-fence rearm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: - In userq restore/rearm, root PD was locked with amdgpu_vm_lock_pd(vm, &exec, 1). - Later in the same drm_exec context, amdgpu_evf_mgr_rearm() attaches BOOKKEEP eviction fences to all locked BOs. - In this flow, root PD validation/update can also consume move-fence slots, so reserving only +1 at PD lock time is not sufficient under stress. Fix: - Reserve caller-side slots at the actual source by switching to: amdgpu_vm_lock_pd(vm, &exec, TTM_NUM_MOVE_FENCES + 1) This keeps reservation policy in callers, consistent with Christian Konig feedback and with amdgpu_gem_object_open() where callers reserve TTM_NUM_MOVE_FENCES + 1 before attaching eviction fences. Credit: - Christian Konig: confirmed fence-slot reservation belongs to callers and asked to investigate why +1 was still not enough. - Prike Liang: clarified the initial variant was workaround-only and developed that initial version independently. Cc: Prike Liang Cc: Sunil Khatri Cc: Christian König Cc: Alex Deucher Signed-off-by: Vitaly Prosyak Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 3938f7b8ec4b..12fbff02e4b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1043,7 +1043,11 @@ amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr) retry_lock: drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 0); drm_exec_until_all_locked(&exec) { - ret = amdgpu_vm_lock_pd(vm, &exec, 1); + /* + * Rearm adds one BOOKKEEP fence and validation may queue move fences + * on the root PD BO, so reserve caller-side slots accordingly. + */ + ret = amdgpu_vm_lock_pd(vm, &exec, TTM_NUM_MOVE_FENCES + 1); drm_exec_retry_on_contention(&exec); if (unlikely(ret)) goto unlock_all; From 6959043d96cfedaabd838cf02ff7b51682e7821e Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 14 Sep 2026 15:54:21 +0100 Subject: [PATCH 088/114] drm/amdgpu: Add SDMA ring init helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidate one part of the SDMA ring initialization with a new amdgpu_sdma_ring_init() helper. Only code which is completely IP version agnostic is moved to the helper. Reviewed-by: Alex Deucher Reviewed-by: Timur Kristóf Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 18 ++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 4 ++++ drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c | 7 +------ drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 7 +------ drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 13 ++----------- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 20 +++++--------------- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 7 +------ drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 6 +----- drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 8 ++------ drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 7 +------ drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 10 +++------- 11 files changed, 39 insertions(+), 68 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 0286e3dd958e..67af55e39e86 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -22,6 +22,7 @@ */ #include +#include #include "amdgpu.h" #include "amdgpu_sdma.h" #include "amdgpu_ras.h" @@ -37,6 +38,23 @@ * GPU SDMA IP block helpers function. */ +int __printf(4, 5) +amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, + unsigned int instance, const char *fmt, ...) +{ + va_list args; + + ring->ring_obj = NULL; + + va_start(args, fmt); + vsnprintf(ring->name, sizeof(ring->name), fmt, args); + va_end(args); + + return amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, + AMDGPU_SDMA_IRQ_INSTANCE0 + instance, + AMDGPU_RING_PRIO_DEFAULT, NULL); +} + struct amdgpu_sdma_instance *amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index 42874b1303ba..f88ec71f49cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -156,6 +156,10 @@ struct amdgpu_buffer_funcs { uint32_t byte_count); }; +int __printf(4, 5) +amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, + unsigned int instance, const char *fmt, ...); + int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id); int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 7c8acdf73d44..4ffc8edee53e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -835,13 +835,8 @@ static int sdma_v2_4_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = false; - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - (i == 0) ? AMDGPU_SDMA_IRQ_INSTANCE0 : - AMDGPU_SDMA_IRQ_INSTANCE1, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index e929d62721b3..8f7d74b44647 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -1116,7 +1116,6 @@ static int sdma_v3_0_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; if (!amdgpu_sriov_vf(adev)) { ring->use_doorbell = true; ring->doorbell_index = adev->doorbell_index.sdma_engine[i]; @@ -1124,11 +1123,7 @@ static int sdma_v3_0_sw_init(struct amdgpu_ip_block *ip_block) ring->use_pollmem = true; } - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - (i == 0) ? AMDGPU_SDMA_IRQ_INSTANCE0 : - AMDGPU_SDMA_IRQ_INSTANCE1, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index bdd140887e10..a652d7b58f95 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -1828,7 +1828,6 @@ static int sdma_v4_0_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; DRM_DEBUG("SDMA %d use_doorbell being set to: [%s]\n", i, @@ -1848,16 +1847,12 @@ static int sdma_v4_0_sw_init(struct amdgpu_ip_block *ip_block) else ring->vm_hub = AMDGPU_MMHUB0(0); - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; if (adev->sdma.has_page_queue) { ring = &adev->sdma.instance[i].page; - ring->ring_obj = NULL; ring->use_doorbell = true; /* paging queue use same doorbell index/routing as gfx queue @@ -1885,11 +1880,7 @@ static int sdma_v4_0_sw_init(struct amdgpu_ip_block *ip_block) else ring->vm_hub = AMDGPU_MMHUB0(0); - sprintf(ring->name, "page%d", i); - r = amdgpu_ring_init(adev, ring, 1024, - &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "page%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 4a1e941cfe2b..ffc0654bed8a 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -1412,7 +1412,6 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block) adev->sdma.instance[i].funcs = &sdma_v4_4_2_sdma_funcs; ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; aid_id = adev->sdma.instance[i].aid_id; @@ -1423,18 +1422,13 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block) ring->doorbell_index = adev->doorbell_index.sdma_engine[i] << 1; ring->vm_hub = AMDGPU_MMHUB0(aid_id); ring->no_user_submission = adev->sdma.no_user_submission; - - sprintf(ring->name, "sdma%d.%d", aid_id, - i % adev->sdma.num_inst_per_aid); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d.%d", aid_id, + i % adev->sdma.num_inst_per_aid); if (r) return r; if (adev->sdma.has_page_queue) { ring = &adev->sdma.instance[i].page; - ring->ring_obj = NULL; ring->use_doorbell = true; /* doorbell index of page queue is assigned right after @@ -1443,13 +1437,9 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block) ring->doorbell_index = (adev->doorbell_index.sdma_engine[i] + 1) << 1; ring->vm_hub = AMDGPU_MMHUB0(aid_id); - - sprintf(ring->name, "page%d.%d", aid_id, - i % adev->sdma.num_inst_per_aid); - r = amdgpu_ring_init(adev, ring, 1024, - &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "page%d.%d", + aid_id, + i % adev->sdma.num_inst_per_aid); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index a4d2a107c74f..3cb1be0836e0 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -1384,7 +1384,6 @@ static int sdma_v5_0_sw_init(struct amdgpu_ip_block *ip_block) mutex_init(&adev->sdma.instance[i].engine_reset_mutex); adev->sdma.instance[i].funcs = &sdma_v5_0_sdma_funcs; ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; DRM_DEBUG("SDMA %d use_doorbell being set to: [%s]\n", i, @@ -1395,11 +1394,7 @@ static int sdma_v5_0_sw_init(struct amdgpu_ip_block *ip_block) : (adev->doorbell_index.sdma_engine[1] << 1); // get DWORD offset ring->vm_hub = AMDGPU_GFXHUB(0); - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - (i == 0) ? AMDGPU_SDMA_IRQ_INSTANCE0 : - AMDGPU_SDMA_IRQ_INSTANCE1, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index e7613e27e774..57ae584ccda8 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -1304,7 +1304,6 @@ static int sdma_v5_2_sw_init(struct amdgpu_ip_block *ip_block) mutex_init(&adev->sdma.instance[i].engine_reset_mutex); adev->sdma.instance[i].funcs = &sdma_v5_2_sdma_funcs; ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; @@ -1315,10 +1314,7 @@ static int sdma_v5_2_sw_init(struct amdgpu_ip_block *ip_block) (adev->doorbell_index.sdma_engine[i] << 1); //get DWORD offset ring->vm_hub = AMDGPU_GFXHUB(0); - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index d9e134b2bc48..e6cc24effe3d 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -1308,7 +1308,6 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; ring->no_user_submission = adev->sdma.no_user_submission; @@ -1320,11 +1319,8 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block) (adev->doorbell_index.sdma_engine[i] << 1); // get DWORD offset ring->vm_hub = AMDGPU_GFXHUB(0); - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, - &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index f3bec3b2d728..aa5c6ed62091 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -1320,7 +1320,6 @@ static int sdma_v7_0_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; ring->no_user_submission = adev->sdma.no_user_submission; @@ -1332,11 +1331,7 @@ static int sdma_v7_0_sw_init(struct amdgpu_ip_block *ip_block) (adev->doorbell_index.sdma_engine[i] << 1); // get DWORD offset ring->vm_hub = AMDGPU_GFXHUB(0); - sprintf(ring->name, "sdma%d", i); - r = amdgpu_ring_init(adev, ring, 1024, - &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d", i); if (r) return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 0f30eb503c2a..f33b0dc17c50 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1305,7 +1305,6 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->sdma.num_instances; i++) { ring = &adev->sdma.instance[i].ring; - ring->ring_obj = NULL; ring->use_doorbell = true; ring->me = i; ring->no_user_submission = adev->sdma.no_user_submission; @@ -1323,12 +1322,9 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) (adev->doorbell_index.sdma_engine[i] << 1); // get DWORD offset ring->vm_hub = AMDGPU_GFXHUB(xcc_id); - sprintf(ring->name, "sdma%d.%d", xcc_id, - GET_INST(SDMA0, i) % adev->sdma.num_inst_per_xcc); - r = amdgpu_ring_init(adev, ring, 1024, - &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + i, - AMDGPU_RING_PRIO_DEFAULT, NULL); + r = amdgpu_sdma_ring_init(adev, ring, i, "sdma%d.%d", xcc_id, + GET_INST(SDMA0, i) % + adev->sdma.num_inst_per_xcc); if (r) return r; } From e42eb28168d49998ad95fde4aaa382969a404628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 8 Sep 2026 20:10:52 +0200 Subject: [PATCH 089/114] drm/amdgpu/sdma: Use common SDMA legacy queue reset on SDMA v4.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now, sdma_v4_4_2_reset_queue() does basically the same as amdgpu_sdma_reset_queue_legacy() so let's use that. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index ffc0654bed8a..f5f3b82b1cf6 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -1569,20 +1569,6 @@ static int sdma_v4_4_2_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static int sdma_v4_4_2_reset_queue(struct amdgpu_ring *ring, - unsigned int vmid, - struct amdgpu_fence *timedout_fence) -{ - struct amdgpu_device *adev = ring->adev; - u32 id = ring->me; - int r; - - amdgpu_amdkfd_suspend(adev, true); - r = amdgpu_sdma_reset_engine(adev, id); - amdgpu_amdkfd_resume(adev, true); - return r; -} - static int sdma_v4_4_2_stop_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; @@ -2026,7 +2012,7 @@ static const struct amdgpu_ring_funcs sdma_v4_4_2_ring_funcs = { .emit_wreg = sdma_v4_4_2_ring_emit_wreg, .emit_reg_wait = sdma_v4_4_2_ring_emit_reg_wait, .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper, - .reset = sdma_v4_4_2_reset_queue, + .reset = amdgpu_sdma_reset_queue_legacy, }; static const struct amdgpu_ring_funcs sdma_v4_4_2_page_ring_funcs = { @@ -2058,7 +2044,7 @@ static const struct amdgpu_ring_funcs sdma_v4_4_2_page_ring_funcs = { .emit_wreg = sdma_v4_4_2_ring_emit_wreg, .emit_reg_wait = sdma_v4_4_2_ring_emit_reg_wait, .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper, - .reset = sdma_v4_4_2_reset_queue, + .reset = amdgpu_sdma_reset_queue_legacy, }; static void sdma_v4_4_2_set_ring_funcs(struct amdgpu_device *adev) From c7071767a50a32ed727cf800ac84372429e3b4b3 Mon Sep 17 00:00:00 2001 From: Dmitriy Chumachenko Date: Mon, 14 Sep 2026 17:33:03 +0300 Subject: [PATCH 090/114] drm/amdgpu: check ras and obj before dereference nbio_v7_9_handle_ras_controller_intr_no_bifring() dereferences ras and obj without checking either for NULL. Both amdgpu_ras_get_context() and amdgpu_ras_find_obj() can return NULL, e.g. during the window between adev->nbio.ras being set (early in amdgpu_ras_init(), by design, to enable the fatal-error interrupt as soon as possible) and the PCIE_BIF ras object actually being created in RAS late_init. Any interrupt in that window crashes in hard-IRQ context. This is analogous to commit d190b459b2a4 ("drm/amdgpu: the warning dereferencing obj for nbio_v7_4"), which fixed the same issue in the nbio_v7_4 handler. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 7692e1ee2446 ("drm/amdgpu: add RAS fatal error handler for NBIO v7.9") Reviewed-by: Tao Zhou Signed-off-by: Dmitriy Chumachenko Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c index bdfd2917e3ca..def02993b7cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c @@ -535,7 +535,7 @@ static void nbio_v7_9_handle_ras_controller_intr_no_bifring(struct amdgpu_device RAS_CNTLR_INTERRUPT_CLEAR, 1); WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl); - if (!ras->disable_ras_err_cnt_harvest) { + if (ras && !ras->disable_ras_err_cnt_harvest && obj) { /* * clear error status after ras_controller_intr * according to hw team and count ue number From acf47ef456b79bf0af1746034513cf6a155be09f Mon Sep 17 00:00:00 2001 From: Ruslan Vagner Date: Tue, 15 Sep 2026 05:55:54 -0700 Subject: [PATCH 091/114] drm/amd: fix comment typos in amdgpu and radeon Fix the spelling in comments. No functional change. Signed-off-by: Ruslan Vagner Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 4 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_job.h | 2 +- drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c | 2 +- drivers/gpu/drm/radeon/radeon_fence.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index d53259a5b82f..acafa8c59cc9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -532,7 +532,7 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev, out->state.flags = 0x0; out->state.hangs = 0x0; - /* determine if a GPU reset has occured since the last call */ + /* determine if a GPU reset has occurred since the last call */ reset_counter = atomic_read(&adev->gpu_reset_counter); /* TODO: this should ideally return NO, GUILTY, or INNOCENT. */ if (ctx->reset_counter_query == reset_counter) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 8c32c6943764..5e424e3b6e71 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -228,12 +228,12 @@ fallback: */ /** - * amdgpu_vmid_had_gpu_reset - check if reset occured since last use + * amdgpu_vmid_had_gpu_reset - check if reset occurred since last use * * @adev: amdgpu_device pointer * @id: VMID structure * - * Check if GPU reset occured since last use of the VMID. + * Check if GPU reset occurred since last use of the VMID. */ bool amdgpu_vmid_had_gpu_reset(struct amdgpu_device *adev, struct amdgpu_vmid *id) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index 44fe40f9e8df..57bd34981be0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -31,7 +31,7 @@ #define AMDGPU_PREAMBLE_IB_PRESENT (1 << 0) /* bit set means preamble IB is first presented in belonging context */ #define AMDGPU_PREAMBLE_IB_PRESENT_FIRST (1 << 1) -/* bit set means context switch occured */ +/* bit set means context switch occurred */ #define AMDGPU_HAVE_CTX_SWITCH (1 << 2) /* bit set means IB is preempted */ #define AMDGPU_IB_PREEMPTED (1 << 3) diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c index 5bc3bc60a2ac..ebbe7900205e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c @@ -46,7 +46,7 @@ static void dcn20_wait_for_vmid_ready(struct dcn20_vmid *vmid) /* According the hardware spec, we need to poll for the lowest * bit of PAGE_TABLE_BASE_ADDR_LO32 = 1 any time a GPUVM * context is updated. We can't use REG_WAIT here since we - * don't have a seperate field to wait on. + * don't have a separate field to wait on. * * TODO: Confirm timeout / poll interval with hardware team */ diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 299865569252..8e9402b8811b 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -191,7 +191,7 @@ static int radeon_fence_check_signaled(wait_queue_entry_t *wait, * @ring: ring index the fence is associated with * * Checks the current fence value and calculates the last - * signalled fence value. Returns true if activity occured + * signalled fence value. Returns true if activity occurred * on the ring, and the fence_queue should be waken up. */ static bool radeon_fence_activity(struct radeon_device *rdev, int ring) @@ -262,7 +262,7 @@ static bool radeon_fence_activity(struct radeon_device *rdev, int ring) * @work: delayed work item * * Checks for fence activity and if there is none probe - * the hardware if a lockup occured. + * the hardware if a lockup occurred. */ static void radeon_fence_check_lockup(struct work_struct *work) { From 5119c2114595b7aadf77649c2b909088a37a86ad Mon Sep 17 00:00:00 2001 From: Omokefe Emmanuel Onanaroghene Date: Tue, 15 Sep 2026 01:54:21 +0100 Subject: [PATCH 092/114] drm/amd/display: fix kernel-doc warnings for amdgpu_display_manager scripts/kernel-doc reports missing and excess member descriptions for struct amdgpu_display_manager: struct member 'power_module' not described struct member 'hdmi_frl_status_polling_wq' not described struct member 'hdmi_frl_status_polling_delay_ms' not described struct member 'boot_time_crc_info' not described Excess struct member 'dm_boot_time_crc_info' description Add @power_module to the main struct doc, document hdmi_frl_status_polling_wq/work/delay_ms with separate inline comments, and rename @dm_boot_time_crc_info to @boot_time_crc_info to match the actual member name. Signed-off-by: Omokefe Emmanuel Onanaroghene Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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 a902a253be45..1d256dce0e4b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -333,6 +333,7 @@ struct hpd_rx_irq_offload_work { * @backlight_link: Link on which to control backlight * @backlight_caps: Capabilities of the backlight device * @freesync_module: Module handling freesync calculations + * @power_module: Module handling power calculations * @hdcp_workqueue: AMDGPU content protection queue * @fw_dmcu: Reference to DMCU firmware * @dmcu_fw_version: Version of the DMCU firmware @@ -731,16 +732,26 @@ struct amdgpu_display_manager { char reply_data[0x40]; // Cannot include dmub_cmd here } fused_io[8]; /** - * @hdmi_frl_status_polling_work: + * @hdmi_frl_status_polling_wq: * * workqueue for 200ms frl status polling */ struct workqueue_struct *hdmi_frl_status_polling_wq; + /** + * @hdmi_frl_status_polling_work: + * + * Delayed work for 200ms HDMI FRL status polling. + */ struct delayed_work hdmi_frl_status_polling_work; + /** + * @hdmi_frl_status_polling_delay_ms: + * + * Delay, in milliseconds, between HDMI FRL status polls. + */ unsigned int hdmi_frl_status_polling_delay_ms; /** - * @dm_boot_time_crc_info: + * @boot_time_crc_info: * * Buffer info for the boot time crc. */ From b2adbdf861e35a8e774f98b3c46dfa9e8dff9196 Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Mon, 14 Sep 2026 18:09:31 -0400 Subject: [PATCH 093/114] drm/amdkfd: set CP_HQD_PQ_CONTROL.SCOPE to SYSTEM for GFX 12.1 On GFX 12.1, ring buffers may be mapped with a non-coherently cached memory type. In that case the queue fetch can observe stale packets, since the packet producers (CPU or other GPUs) do not keep the device-side cache coherent. Set CP_HQD_PQ_CONTROL.SCOPE=3 (SYSTEM) so the queue fetches use system scope and pick up the latest packets. Apply this to compute queue MQDs and to MES-managed queues. Without this, the queue can consume stale packets for long periods, causing severe slowdowns and, in the worst case, application hangs. Reviewed-by: Harish Kasiviswanathan Signed-off-by: Joseph Greathouse Signed-off-by: Mukul Joshi Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/mes_v12_1.c | 1 + drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index a69f9b9c5cd6..7459547b7a69 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -1302,6 +1302,7 @@ static int mes_v12_1_mqd_init(struct amdgpu_ring *ring) tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, RPTR_BLOCK_SIZE, ((order_base_2(AMDGPU_GPU_PAGE_SIZE / 4) - 1) << 8)); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, SCOPE, 3); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TUNNEL_DISPATCH, 0); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index 60de32cb9ecd..29c20398c60b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -335,6 +335,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, /* GC 10 removed WPP_CLAMP from PQ Control */ m->cp_hqd_pq_control |= CP_HQD_PQ_CONTROL__NO_UPDATE_RPTR_MASK | 2 << CP_HQD_PQ_CONTROL__SLOT_BASED_WPTR__SHIFT | + 3 << CP_HQD_PQ_CONTROL__SCOPE__SHIFT | 1 << CP_HQD_PQ_CONTROL__QUEUE_FULL_EN__SHIFT; m->cp_hqd_pq_doorbell_control |= 1 << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_BIF_DROP__SHIFT; From f35aa5c77f5fca20fc269d6221df19c7b96704a5 Mon Sep 17 00:00:00 2001 From: Hari Mishal Date: Mon, 14 Sep 2026 15:29:20 +0200 Subject: [PATCH 094/114] drm/amd/pm: bound SCLK FCW range entries to the table size atomctrl_get_smc_sclk_range_table() copies ucSclkEntryNum entries from the VBIOS SMU_Info table into pp_atom_ctrl_sclk_range_table. Both the source (asSclkFcwRangeEntry[]) and destination (table->entry[]) arrays are fixed at MAX_SCLK_RANGE (8) entries, but ucSclkEntryNum is an unchecked u8 read straight from the VBIOS, so it can be up to 255. The destination is stack-allocated in both callers (polaris10_get_sclk_range_table(), vegam_get_sclk_range_table()), so a count above 8 overflows a stack buffer, not just a heap read. Reject counts above MAX_SCLK_RANGE rather than clamping to it. Only one version of this VBIOS struct (ATOM_SMU_INFO_V2_1) has ever existed, so a count above 8 corresponds to no legitimate table format and means either a corrupted or a tampered VBIOS. Fixes: a23eefa2f461 ("drm/amd/powerplay: enable dpm for baffin.") Assisted-by: gkh_clanker_t1000 Signed-off-by: Hari Mishal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c index 4b796d60b03d..3217ad3c1d17 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c @@ -1025,6 +1025,9 @@ int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_atom_ctr if (!psmu_info) return -EINVAL; + if (psmu_info->ucSclkEntryNum > MAX_SCLK_RANGE) + return -EINVAL; + for (i = 0; i < psmu_info->ucSclkEntryNum; i++) { table->entry[i].ucVco_setting = psmu_info->asSclkFcwRangeEntry[i].ucVco_setting; table->entry[i].ucPostdiv = psmu_info->asSclkFcwRangeEntry[i].ucPostdiv; From ce23f00eb12b005215d502e666da512021f86513 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 14 Sep 2026 15:54:22 +0100 Subject: [PATCH 095/114] drm/amdgpu: Add amdgpu_sdma_types.h header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is an include loop between amdgpu.h and amdgpu_sdma.h which prevents adding any interesting static inline helpers to the latter. Break it by moving the type definitions to a new amdgpu_sdma_types.h header. This is required so static inline helpers which need to dereference struct amdgpu_device can be added to amdgpu_sdma.h. Reviewed-by: Alex Deucher Reviewed-by: Timur Kristóf Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 138 ++------------- .../gpu/drm/amd/amdgpu/amdgpu_sdma_types.h | 161 ++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c | 1 + drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c | 1 + drivers/gpu/drm/amd/amdgpu/cik_sdma.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 1 + drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 2 + 18 files changed, 189 insertions(+), 129 deletions(-) create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 79b69d74eb2e..5811709b33b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -79,7 +79,7 @@ #include "amdgpu_umsch_mm.h" #include "amdgpu_gmc.h" #include "amdgpu_gfx.h" -#include "amdgpu_sdma.h" +#include "amdgpu_sdma_types.h" #include "amdgpu_lsdma.h" #include "amdgpu_nbio.h" #include "amdgpu_reg_access.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index d80fe17556da..d8786a93a851 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -36,6 +36,7 @@ #include "amdgpu_rap.h" #include "amdgpu_securedisplay.h" #include "amdgpu_fw_attestation.h" +#include "amdgpu_sdma.h" #include "amdgpu_umr.h" #include "amdgpu_reset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 7c0554506334..1bf2a42fa63c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -33,6 +33,8 @@ #include "amdgpu_gmc.h" #include "amdgpu_ras.h" #include "amdgpu_reset.h" +#include "amdgpu_ring.h" +#include "amdgpu_sdma.h" #include "amdgpu_xgmi.h" #include "amdgpu_atomfirmware.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index f88ec71f49cb..3a532a4a19ed 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -23,139 +23,21 @@ #ifndef __AMDGPU_SDMA_H__ #define __AMDGPU_SDMA_H__ -#include "amdgpu_ras.h" -/* max number of IP instances */ -#define AMDGPU_MAX_SDMA_INSTANCES 16 +#include "amdgpu_sdma_types.h" +#include "amdgpu.h" +#include "amdgpu_ring.h" -enum amdgpu_sdma_irq { - AMDGPU_SDMA_IRQ_INSTANCE0 = 0, - AMDGPU_SDMA_IRQ_INSTANCE1, - AMDGPU_SDMA_IRQ_INSTANCE2, - AMDGPU_SDMA_IRQ_INSTANCE3, - AMDGPU_SDMA_IRQ_INSTANCE4, - AMDGPU_SDMA_IRQ_INSTANCE5, - AMDGPU_SDMA_IRQ_INSTANCE6, - AMDGPU_SDMA_IRQ_INSTANCE7, - AMDGPU_SDMA_IRQ_INSTANCE8, - AMDGPU_SDMA_IRQ_INSTANCE9, - AMDGPU_SDMA_IRQ_INSTANCE10, - AMDGPU_SDMA_IRQ_INSTANCE11, - AMDGPU_SDMA_IRQ_INSTANCE12, - AMDGPU_SDMA_IRQ_INSTANCE13, - AMDGPU_SDMA_IRQ_INSTANCE14, - AMDGPU_SDMA_IRQ_INSTANCE15, - AMDGPU_SDMA_IRQ_LAST -}; +#include + +struct amdgpu_device; +struct amdgpu_iv_entry; +struct amdgpu_irq_src; +struct amdgpu_ring; +struct ras_common_if; #define NUM_SDMA(x) hweight32(x) -struct amdgpu_sdma_csa_info { - u32 size; - u32 alignment; -}; - -struct amdgpu_sdma_funcs { - int (*stop_kernel_queue)(struct amdgpu_ring *ring); - int (*start_kernel_queue)(struct amdgpu_ring *ring); - int (*soft_reset_kernel_queue)(struct amdgpu_device *adev, u32 instance_id); - bool (*detect_hung_queue)(struct amdgpu_device *adev, u32 doorbell_index, - u32 *instance_id, u32 *queue_id); -}; - -struct amdgpu_sdma_instance { - /* SDMA firmware */ - const struct firmware *fw; - uint32_t fw_version; - uint32_t feature_version; - - struct amdgpu_ring ring; - struct amdgpu_ring page; - bool burst_nop; - union { - uint32_t aid_id; - uint32_t xcc_id; - }; - - struct amdgpu_bo *sdma_fw_obj; - uint64_t sdma_fw_gpu_addr; - uint32_t *sdma_fw_ptr; - struct mutex engine_reset_mutex; - const struct amdgpu_sdma_funcs *funcs; -}; - -struct amdgpu_sdma_ras { - struct amdgpu_ras_block_object ras_block; -}; - -struct amdgpu_sdma { - struct amdgpu_sdma_instance instance[AMDGPU_MAX_SDMA_INSTANCES]; - struct amdgpu_irq_src trap_irq; - struct amdgpu_irq_src illegal_inst_irq; - struct amdgpu_irq_src fence_irq; - struct amdgpu_irq_src ecc_irq; - struct amdgpu_irq_src vm_hole_irq; - struct amdgpu_irq_src doorbell_invalid_irq; - struct amdgpu_irq_src pool_timeout_irq; - struct amdgpu_irq_src srbm_write_irq; - struct amdgpu_irq_src ctxt_empty_irq; - - int num_instances; - uint32_t sdma_mask; - bool sdma_debug; - union { - int num_inst_per_aid; - int num_inst_per_xcc; - }; - bool has_page_queue; - struct ras_common_if *ras_if; - struct amdgpu_sdma_ras *ras; - uint32_t *ip_dump; - uint32_t supported_reset; - struct list_head reset_callback_list; - bool no_user_submission; - bool disable_uq; - void (*get_csa_info)(struct amdgpu_device *adev, - struct amdgpu_sdma_csa_info *csa_info); -}; - -/* - * Provided by hw blocks that can move/clear data. e.g., gfx or sdma - * But currently, we use sdma to move data. - */ -struct amdgpu_buffer_funcs { - /* maximum bytes in a single operation */ - uint32_t copy_max_bytes; - - /* number of dw to reserve per operation */ - unsigned copy_num_dw; - - /* used for buffer migration */ - void (*emit_copy_buffer)(struct amdgpu_ib *ib, - /* src addr in bytes */ - uint64_t src_offset, - /* dst addr in bytes */ - uint64_t dst_offset, - /* number of byte to transfer */ - uint32_t byte_count, - uint32_t copy_flags); - - /* maximum bytes in a single operation */ - uint32_t fill_max_bytes; - - /* number of dw to reserve per operation */ - unsigned fill_num_dw; - - /* used for buffer clearing */ - void (*emit_fill_buffer)(struct amdgpu_ib *ib, - /* value to write to memory */ - uint32_t src_data, - /* dst addr in bytes */ - uint64_t dst_offset, - /* number of byte to fill */ - uint32_t byte_count); -}; - int __printf(4, 5) amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, unsigned int instance, const char *fmt, ...); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h new file mode 100644 index 000000000000..bb0aa0c20314 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h @@ -0,0 +1,161 @@ +/* + * Copyright 2018 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. + * + */ + +#ifndef __AMDGPU_SDMA_TYPES_H__ +#define __AMDGPU_SDMA_TYPES_H__ +#include "amdgpu_ras.h" + +/* max number of IP instances */ +#define AMDGPU_MAX_SDMA_INSTANCES 16 + +enum amdgpu_sdma_irq { + AMDGPU_SDMA_IRQ_INSTANCE0 = 0, + AMDGPU_SDMA_IRQ_INSTANCE1, + AMDGPU_SDMA_IRQ_INSTANCE2, + AMDGPU_SDMA_IRQ_INSTANCE3, + AMDGPU_SDMA_IRQ_INSTANCE4, + AMDGPU_SDMA_IRQ_INSTANCE5, + AMDGPU_SDMA_IRQ_INSTANCE6, + AMDGPU_SDMA_IRQ_INSTANCE7, + AMDGPU_SDMA_IRQ_INSTANCE8, + AMDGPU_SDMA_IRQ_INSTANCE9, + AMDGPU_SDMA_IRQ_INSTANCE10, + AMDGPU_SDMA_IRQ_INSTANCE11, + AMDGPU_SDMA_IRQ_INSTANCE12, + AMDGPU_SDMA_IRQ_INSTANCE13, + AMDGPU_SDMA_IRQ_INSTANCE14, + AMDGPU_SDMA_IRQ_INSTANCE15, + AMDGPU_SDMA_IRQ_LAST +}; + +struct amdgpu_sdma_csa_info { + u32 size; + u32 alignment; +}; + +struct amdgpu_sdma_funcs { + int (*stop_kernel_queue)(struct amdgpu_ring *ring); + int (*start_kernel_queue)(struct amdgpu_ring *ring); + int (*soft_reset_kernel_queue)(struct amdgpu_device *adev, u32 instance_id); + bool (*detect_hung_queue)(struct amdgpu_device *adev, u32 doorbell_index, + u32 *instance_id, u32 *queue_id); +}; + +struct amdgpu_sdma_instance { + /* SDMA firmware */ + const struct firmware *fw; + uint32_t fw_version; + uint32_t feature_version; + + struct amdgpu_ring ring; + struct amdgpu_ring page; + bool burst_nop; + union { + uint32_t aid_id; + uint32_t xcc_id; + }; + + struct amdgpu_bo *sdma_fw_obj; + uint64_t sdma_fw_gpu_addr; + uint32_t *sdma_fw_ptr; + struct mutex engine_reset_mutex; + /* track guilty state of GFX and PAGE queues */ + bool gfx_guilty; + bool page_guilty; + const struct amdgpu_sdma_funcs *funcs; +}; + +struct amdgpu_sdma_ras { + struct amdgpu_ras_block_object ras_block; +}; + +struct amdgpu_sdma { + struct amdgpu_sdma_instance instance[AMDGPU_MAX_SDMA_INSTANCES]; + struct amdgpu_irq_src trap_irq; + struct amdgpu_irq_src illegal_inst_irq; + struct amdgpu_irq_src fence_irq; + struct amdgpu_irq_src ecc_irq; + struct amdgpu_irq_src vm_hole_irq; + struct amdgpu_irq_src doorbell_invalid_irq; + struct amdgpu_irq_src pool_timeout_irq; + struct amdgpu_irq_src srbm_write_irq; + struct amdgpu_irq_src ctxt_empty_irq; + + int num_instances; + uint32_t sdma_mask; + bool sdma_debug; + union { + int num_inst_per_aid; + int num_inst_per_xcc; + }; + uint32_t srbm_soft_reset; + bool has_page_queue; + struct ras_common_if *ras_if; + struct amdgpu_sdma_ras *ras; + uint32_t *ip_dump; + uint32_t supported_reset; + struct list_head reset_callback_list; + bool no_user_submission; + bool disable_uq; + void (*get_csa_info)(struct amdgpu_device *adev, + struct amdgpu_sdma_csa_info *csa_info); +}; + +/* + * Provided by hw blocks that can move/clear data. e.g., gfx or sdma + * But currently, we use sdma to move data. + */ +struct amdgpu_buffer_funcs { + /* maximum bytes in a single operation */ + uint32_t copy_max_bytes; + + /* number of dw to reserve per operation */ + unsigned copy_num_dw; + + /* used for buffer migration */ + void (*emit_copy_buffer)(struct amdgpu_ib *ib, + /* src addr in bytes */ + uint64_t src_offset, + /* dst addr in bytes */ + uint64_t dst_offset, + /* number of byte to transfer */ + uint32_t byte_count, + uint32_t copy_flags); + + /* maximum bytes in a single operation */ + uint32_t fill_max_bytes; + + /* number of dw to reserve per operation */ + unsigned fill_num_dw; + + /* used for buffer clearing */ + void (*emit_fill_buffer)(struct amdgpu_ib *ib, + /* value to write to memory */ + uint32_t src_data, + /* dst addr in bytes */ + uint64_t dst_offset, + /* number of byte to fill */ + uint32_t byte_count); +}; + +#endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c index f4a4432c2c5d..b2c2650b0149 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c @@ -26,6 +26,7 @@ #include #include #include "amdgpu.h" +#include "amdgpu_sdma.h" #include "amdgpu_ualink.h" #include "amdgpu_xgmi.h" #include "amdgpu_dma_buf.h" diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c index cddfe4015f53..9a0c12b1a309 100644 --- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c +++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c @@ -30,6 +30,7 @@ #include "gfxhub_v1_2.h" #include "sdma_v4_4_2.h" #include "amdgpu_ip.h" +#include "amdgpu_sdma.h" void aqua_vanjaram_doorbell_index_init(struct amdgpu_device *adev) { diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index f444207fa550..8a09fa912931 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -27,6 +27,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "cikd.h" #include "cik.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 4ffc8edee53e..22b24ef8351e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "vi.h" #include "vid.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 8f7d74b44647..d1c29c88af7e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "vi.h" #include "vid.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index a652d7b58f95..24cb23070a2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "sdma0/sdma0_4_2_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index f5f3b82b1cf6..00295a1fce23 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -30,6 +30,7 @@ #include "amdgpu_xcp.h" #include "amdgpu_ucode.h" #include "amdgpu_trace.h" +#include "amdgpu_sdma.h" #include "amdgpu_reset.h" #include "sdma/sdma_4_4_2_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index 3cb1be0836e0..dec1cc72ad66 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "gc/gc_10_1_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index 57ae584ccda8..ec23fab229d9 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "gc/gc_10_3_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index e6cc24effe3d..4205794d61b4 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "gc/gc_11_0_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index aa5c6ed62091..46ecf0929eda 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "gc/gc_12_0_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index f33b0dc17c50..916e92bae535 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_ucode.h" +#include "amdgpu_sdma.h" #include "amdgpu_trace.h" #include "gc/gc_12_1_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 6b64b1692235..8b96e1fe741a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -24,6 +24,8 @@ #include #include #include +#include "amdgpu.h" +#include "amdgpu_sdma.h" #include "amdgpu_sync.h" #include "amdgpu_object.h" #include "amdgpu_vm.h" From 5d7ffdc7f8f55b4d7a91a7ebedbbc0c2ffec9076 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 14 Sep 2026 15:54:23 +0100 Subject: [PATCH 096/114] drm/amdgpu: Convert SDMA instance and index to direct lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of searching for the matching SDMA instance inside many of the SDMA ring vfuncs lets just use the instance number already stored in the ring and replace the function calls and array walks with direct lookups. As these helpers are used only from SDMA vfuncs we can remove safely remove the "is this a SDMA ring" checks as well. We could add an assert that ring->type queried is AMDGPU_RING_TYPE_SDMA but as currently there are no debug build only assert macros in this driver for now I opted not to so the helper can stay a trivial static inline without polluting the callers with out of line logging. Reviewed-by: Alex Deucher Reviewed-by: Timur Kristóf Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 45 +++--------------------- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 9 +++-- drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c | 6 ++-- drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 6 ++-- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 6 ++-- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 6 ++-- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 10 +++--- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 12 +++---- drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 12 +++---- drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 12 +++---- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 12 +++---- 11 files changed, 55 insertions(+), 81 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 67af55e39e86..aa3428ed7aa6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -55,57 +55,20 @@ amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, AMDGPU_RING_PRIO_DEFAULT, NULL); } -struct amdgpu_sdma_instance *amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring) -{ - struct amdgpu_device *adev = ring->adev; - int i; - - for (i = 0; i < adev->sdma.num_instances; i++) - if (ring == &adev->sdma.instance[i].ring || - ring == &adev->sdma.instance[i].page) - return &adev->sdma.instance[i]; - - return NULL; -} - -int amdgpu_sdma_get_index_from_ring(struct amdgpu_ring *ring, uint32_t *index) -{ - struct amdgpu_device *adev = ring->adev; - int i; - - for (i = 0; i < adev->sdma.num_instances; i++) { - if (ring == &adev->sdma.instance[i].ring || - ring == &adev->sdma.instance[i].page) { - *index = i; - return 0; - } - } - - return -EINVAL; -} - uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, unsigned int vmid) { struct amdgpu_device *adev = ring->adev; - uint64_t csa_mc_addr; - uint32_t index = 0; - int r; /* don't enable OS preemption on SDMA under SRIOV */ if (amdgpu_sriov_vf(adev) || vmid == 0 || !adev->gfx.mcbp) return 0; - r = amdgpu_sdma_get_index_from_ring(ring, &index); + if (ring->me > 31) + return 0; - if (r || index > 31) - csa_mc_addr = 0; - else - csa_mc_addr = amdgpu_csa_vaddr(adev) + - AMDGPU_CSA_SDMA_OFFSET + - index * AMDGPU_CSA_SDMA_SIZE; - - return csa_mc_addr; + return amdgpu_csa_vaddr(adev) + AMDGPU_CSA_SDMA_OFFSET + + ring->me * AMDGPU_CSA_SDMA_SIZE; } int amdgpu_sdma_ras_late_init(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index 3a532a4a19ed..ed08d521b363 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -51,9 +51,12 @@ int amdgpu_sdma_reset_queue_legacy(struct amdgpu_ring *ring, #define amdgpu_emit_copy_buffer(adev, ib, s, d, b, t) (adev)->mman.buffer_funcs->emit_copy_buffer((ib), (s), (d), (b), (t)) #define amdgpu_emit_fill_buffer(adev, ib, s, d, b) (adev)->mman.buffer_funcs->emit_fill_buffer((ib), (s), (d), (b)) -struct amdgpu_sdma_instance * -amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring); -int amdgpu_sdma_get_index_from_ring(struct amdgpu_ring *ring, uint32_t *index); +static inline struct amdgpu_sdma_instance * +amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring) +{ + return &ring->adev->sdma.instance[ring->me]; +} + uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, unsigned vmid); int amdgpu_sdma_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 22b24ef8351e..95613013e07c 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -224,10 +224,11 @@ static void sdma_v2_4_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v2_4_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -709,12 +710,13 @@ static void sdma_v2_4_vm_set_pte_pde(struct amdgpu_ib *ib, uint64_t pe, static void sdma_v2_4_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index d1c29c88af7e..835c0278fd87 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -401,10 +401,11 @@ static void sdma_v3_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v3_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -983,12 +984,13 @@ static void sdma_v3_0_vm_set_pte_pde(struct amdgpu_ib *ib, uint64_t pe, static void sdma_v3_0_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index 24cb23070a2b..dc75755abae6 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -784,10 +784,11 @@ static void sdma_v4_0_page_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v4_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1643,12 +1644,13 @@ static void sdma_v4_0_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v4_0_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 00295a1fce23..ebf3a9a454c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -349,10 +349,11 @@ static void sdma_v4_4_2_page_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v4_4_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1198,12 +1199,13 @@ static void sdma_v4_4_2_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v4_4_2_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index dec1cc72ad66..a8d27f1944a2 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -407,10 +407,11 @@ static void sdma_v5_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v5_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1214,12 +1215,13 @@ static void sdma_v5_0_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v5_0_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 0x7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); @@ -1563,11 +1565,9 @@ static int sdma_v5_0_ring_preempt_ib(struct amdgpu_ring *ring) { int i, r = 0; struct amdgpu_device *adev = ring->adev; - u32 index = 0; u64 sdma_gfx_preempt; - amdgpu_sdma_get_index_from_ring(ring, &index); - if (index == 0) + if (ring->me == 0) sdma_gfx_preempt = mmSDMA0_GFX_PREEMPT; else sdma_gfx_preempt = mmSDMA1_GFX_PREEMPT; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index ec23fab229d9..39660b36b998 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -255,10 +255,11 @@ static void sdma_v5_2_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v5_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1115,12 +1116,13 @@ static void sdma_v5_2_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v5_2_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 0x7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); @@ -1486,12 +1488,10 @@ static int sdma_v5_2_ring_preempt_ib(struct amdgpu_ring *ring) { int i, r = 0; struct amdgpu_device *adev = ring->adev; - u32 index = 0; u64 sdma_gfx_preempt; - amdgpu_sdma_get_index_from_ring(ring, &index); - sdma_gfx_preempt = - sdma_v5_2_get_reg_offset(adev, index, mmSDMA0_GFX_PREEMPT); + sdma_gfx_preempt = sdma_v5_2_get_reg_offset(adev, ring->me, + mmSDMA0_GFX_PREEMPT); /* assert preemption condition */ amdgpu_ring_set_preempt_cond_exec(ring, false); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index 4205794d61b4..e940401f814e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -243,10 +243,11 @@ static void sdma_v6_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v6_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1105,12 +1106,13 @@ static void sdma_v6_0_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v6_0_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 0x7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); @@ -1485,12 +1487,10 @@ static int sdma_v6_0_ring_preempt_ib(struct amdgpu_ring *ring) { int i, r = 0; struct amdgpu_device *adev = ring->adev; - u32 index = 0; u64 sdma_gfx_preempt; - amdgpu_sdma_get_index_from_ring(ring, &index); - sdma_gfx_preempt = - sdma_v6_0_get_reg_offset(adev, index, regSDMA0_QUEUE0_PREEMPT); + sdma_gfx_preempt = sdma_v6_0_get_reg_offset(adev, ring->me, + regSDMA0_QUEUE0_PREEMPT); /* assert preemption condition */ amdgpu_ring_set_preempt_cond_exec(ring, false); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index 46ecf0929eda..3dee4d17eef3 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -245,10 +245,11 @@ static void sdma_v7_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v7_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1152,12 +1153,13 @@ static void sdma_v7_0_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v7_0_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 0x7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); @@ -1466,12 +1468,10 @@ static int sdma_v7_0_ring_preempt_ib(struct amdgpu_ring *ring) { int i, r = 0; struct amdgpu_device *adev = ring->adev; - u32 index = 0; u64 sdma_gfx_preempt; - amdgpu_sdma_get_index_from_ring(ring, &index); - sdma_gfx_preempt = - sdma_v7_0_get_reg_offset(adev, index, regSDMA0_QUEUE0_PREEMPT); + sdma_gfx_preempt = sdma_v7_0_get_reg_offset(adev, ring->me, + regSDMA0_QUEUE0_PREEMPT); /* assert preemption condition */ amdgpu_ring_set_preempt_cond_exec(ring, false); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 916e92bae535..934e84cb81e2 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -239,10 +239,11 @@ static void sdma_v7_1_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v7_1_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; int i; for (i = 0; i < count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) amdgpu_ring_write(ring, ring->funcs->nop | SDMA_PKT_NOP_HEADER_COUNT(count - 1)); else @@ -1162,12 +1163,13 @@ static void sdma_v7_1_vm_set_pte_pde(struct amdgpu_ib *ib, static void sdma_v7_1_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); + const bool burst_nop = sdma->burst_nop; u32 pad_count; int i; pad_count = (-ib->length_dw) & 0x7; for (i = 0; i < pad_count; i++) - if (sdma && sdma->burst_nop && (i == 0)) + if (i == 0 && burst_nop) ib->ptr[ib->length_dw++] = SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_NOP) | SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1); @@ -1453,12 +1455,10 @@ static int sdma_v7_1_ring_preempt_ib(struct amdgpu_ring *ring) { int i, r = 0; struct amdgpu_device *adev = ring->adev; - u32 index = 0; u64 sdma_gfx_preempt; - amdgpu_sdma_get_index_from_ring(ring, &index); - sdma_gfx_preempt = - sdma_v7_1_get_reg_offset(adev, index, regSDMA0_SDMA_QUEUE0_PREEMPT); + sdma_gfx_preempt = sdma_v7_1_get_reg_offset(adev, ring->me, + regSDMA0_SDMA_QUEUE0_PREEMPT); /* assert preemption condition */ amdgpu_ring_set_preempt_cond_exec(ring, false); From 7ea6a47224e2c6e89a3a682d7fbaace4817a55aa Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 15 Sep 2026 10:59:45 -0500 Subject: [PATCH 097/114] drm/amdgpu: Fix GPU PCIe link capability reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit eb53125a7ad9 ("drm/amd: Add dedicated helper for amdgpu_device_find_parent()") made amdgpu_device_gpu_bandwidth() query the first device outside the dGPU. That is the host side of the physical link, not the GPU side. As a result, the ASIC and platform capability masks can both be based on the host port. drm_amdgpu_info_device then exposes the host capabilities to userspace, such as Gen5 x16 for a Gen4 x8 GPU. Cache both ends of the physical link during device initialization. Use link_dev for the GPU capability and link_partner for the platform capability and _PR3 detection. Reported-by: "Marek Olšák" Closes: https://lore.kernel.org/amd-gfx/CAAxE2A4VhsAzzO1QjBjUg+NgnbD04ZzMyN6xsUJxjKJHH6hxiw@mail.gmail.com/ Suggested-by: Lijo Lazar Fixes: eb53125a7ad9 ("drm/amd: Add dedicated helper for amdgpu_device_find_parent()") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 48 ++++++++-------------- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 5811709b33b7..6c1c775256fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -626,6 +626,9 @@ enum amdgpu_enforce_isolation_mode { struct amdgpu_device { struct device *dev; struct pci_dev *pdev; + /* The two ends of the physical PCIe link outside the device. */ + struct pci_dev *link_dev; + struct pci_dev *link_partner; struct drm_device ddev; #ifdef CONFIG_DRM_AMD_ACP diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 0426118a9279..1a2dda891c0e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1959,18 +1959,17 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev) adev->uid_info = NULL; } -static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev) +static void amdgpu_device_init_pcie_links(struct amdgpu_device *adev) { - struct pci_dev *parent = adev->pdev; + adev->link_dev = adev->pdev; + adev->link_partner = pci_upstream_bridge(adev->link_dev); - /* skip upstream/downstream switches internal to dGPU */ - while ((parent = pci_upstream_bridge(parent))) { - if (parent->vendor == PCI_VENDOR_ID_ATI) - continue; - break; + /* Skip upstream/downstream switches internal to the dGPU. */ + while (adev->link_partner && + adev->link_partner->vendor == PCI_VENDOR_ID_ATI) { + adev->link_dev = adev->link_partner; + adev->link_partner = pci_upstream_bridge(adev->link_dev); } - - return parent; } /** @@ -1986,7 +1985,6 @@ static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev) static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) { struct amdgpu_ip_block *ip_block; - struct pci_dev *parent; bool total, skip_bios, early_full_gpu_access = false; uint32_t bios_flags; int i, r; @@ -2082,10 +2080,9 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) !dev_is_removable(&adev->pdev->dev)) adev->flags |= AMD_IS_PX; - if (!(adev->flags & AMD_IS_APU)) { - parent = amdgpu_device_find_parent(adev); - adev->has_pr3 = parent ? pci_pr3_present(parent) : false; - } + if (!(adev->flags & AMD_IS_APU)) + adev->has_pr3 = adev->link_partner && + pci_pr3_present(adev->link_partner); adev->pm.pp_feature = amdgpu_pp_feature_mask; if (amdgpu_sriov_vf(adev) || sched_policy == KFD_SCHED_POLICY_NO_HWS) @@ -3784,6 +3781,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, adev->shutdown = false; adev->flags = flags; + amdgpu_device_init_pcie_links(adev); if (amdgpu_force_asic_type >= 0 && amdgpu_force_asic_type < CHIP_LAST) adev->asic_type = amdgpu_force_asic_type; @@ -5881,11 +5879,9 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev, *width = PCIE_LNK_WIDTH_UNKNOWN; if (amdgpu_device_pcie_dynamic_switching_supported(adev)) { - struct pci_dev *parent = amdgpu_device_find_parent(adev); - - if (parent) { - *speed = pcie_get_speed_cap(parent); - *width = pcie_get_width_cap(parent); + if (adev->link_partner) { + *speed = pcie_get_speed_cap(adev->link_partner); + *width = pcie_get_width_cap(adev->link_partner); } } else { /* use the current speeds rather than max if switching is not supported */ @@ -5907,21 +5903,11 @@ static void amdgpu_device_gpu_bandwidth(struct amdgpu_device *adev, enum pci_bus_speed *speed, enum pcie_link_width *width) { - struct pci_dev *parent = adev->pdev; - if (!speed || !width) return; - /* use the device itself */ - *speed = pcie_get_speed_cap(adev->pdev); - *width = pcie_get_width_cap(adev->pdev); - - /* use the link outside the device */ - parent = amdgpu_device_find_parent(adev); - if (parent) { - *speed = pcie_get_speed_cap(parent); - *width = pcie_get_width_cap(parent); - } + *speed = pcie_get_speed_cap(adev->link_dev); + *width = pcie_get_width_cap(adev->link_dev); } /** From 4f18c56630383c14bfc6b2d65f88f2f895d2121a Mon Sep 17 00:00:00 2001 From: David Francis Date: Wed, 5 Aug 2026 09:51:35 -0400 Subject: [PATCH 098/114] drm/amdkfd: Avoid integer underflow with ffs in EOP ring size calc The low 6 bits of cp_hqd_eop_control store the base-2 logarithm of the EOP ring size. This was calculated as ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1 But ffs can in theory return 1 or 0, so this could underflow (although in practice the ring buffer size cannot be less than 4096). Change this to ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4) using properties of logarithms. Reviewed-by: Kent Russell Signed-off-by: David Francis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index e034da638c07..4f8a8a1a6186 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -204,7 +204,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * is safe, giving a maximum field value of 0xA. */ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; + ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 350fcbbba4b2..bf015dc5b868 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -242,7 +242,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * is safe, giving a maximum field value of 0xA. */ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; + ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c index 63f25a60baa5..6ea09b031caf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c @@ -217,7 +217,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * is safe, giving a maximum field value of 0xA. */ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; + ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index 29c20398c60b..ef74d62c9d56 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -321,7 +321,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * is safe, giving a maximum field value of 0xA. */ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; + ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = From f0f43fcf8b2b3a924cad9444340921c96ed5f634 Mon Sep 17 00:00:00 2001 From: David Francis Date: Wed, 5 Aug 2026 09:16:51 -0400 Subject: [PATCH 099/114] drm/amdkfd: Avoid integer underflow in EOP ring size calculation. The low 6 bits of cp_hqd_eop_control store the base-2 logarithm of the EOP ring size. This was calculated as order_base_2(q->eop_ring_buffer_size / 4) - 1 But order_base_2 can in theory return 0, so this could underflow (although in practice the ring buffer size cannot be less than 4096). Change this to order_base_2(q->eop_ring_buffer_size / 8) using properties of logarithms. Also add to the above comment to make the mathematics more clear. Reviewed-by: Kent Russell Signed-off-by: David Francis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 6 +++++- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index b95720198e28..6e6bc1ec0b64 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -285,6 +285,10 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, 1 << CP_HQD_IB_CONTROL__IB_EXE_DISABLE__SHIFT; /* + * The lowest 6 bits of eop_control store the EOP ring size. If + * their value is X, the ring size is 2^(X + 1) dwords, or + * 2^(X + 3) bytes. + * * HW does not clamp this field correctly. Maximum EOP queue size * is constrained by per-SE EOP done signal count, which is 8-bit. * Limit is 0xFF EOP entries (= 0x7F8 dwords). CP will not submit @@ -296,7 +300,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * */ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? - min(0xA, order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0; + min(0xA, order_base_2(q->eop_ring_buffer_size / 8)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index 60b87a500698..029572548c14 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -208,6 +208,9 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd, mtype << CP_HQD_IB_CONTROL__MTYPE__SHIFT; /* + * The lowest 6 bits of eop_control store the EOP ring size. If + * their value is X, the ring size is 2^(X + 1) dwords, or + * 2^(X + 3) bytes. * HW does not clamp this field correctly. Maximum EOP queue size * is constrained by per-SE EOP done signal count, which is 8-bit. * Limit is 0xFF EOP entries (= 0x7F8 dwords). CP will not submit @@ -215,7 +218,7 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd, * is safe, giving a maximum field value of 0xA. */ m->cp_hqd_eop_control |= q->eop_ring_buffer_size ? min(0xA, - order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0; + order_base_2(q->eop_ring_buffer_size / 8)) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = From 325c9a827cdd748e126eafeadaffc556204773d2 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Tue, 15 Sep 2026 14:07:39 +0530 Subject: [PATCH 100/114] drm/amd/display: Fix NULL dereference in dcn50/dcn60 init_hw dc->clk_mgr is checked for NULL earlier in dcn50_init_hw() and dcn60_init_hw(), but dcn50_initialize_min_clocks() and dcn401_initialize_min_clocks() are called without any guard, causing Smatch to report potential NULL dereferences. Guard both call sites with the same pattern used throughout both functions: if (dc->clk_mgr && dc->clk_mgr->funcs) Also fix dcn50_initialize_min_clocks() which calls get_dispclk_from_dentist without checking the function pointer, unlike the dcn401 equivalent which guards that call. Fix kernel-doc in dcn60_hwseq.c by adding missing parameter descriptions for @probe in dcn60_update_probe_status() and @type in is_probe_measurement_type_for_hubbub(). Fixes: 7f7d7ea1fa51 ("drm/amd/display: Add new sources for DCN6") Reported-by: Dan Carpenter Cc: Aurabindo Pillai Cc: Ivan Lipski Cc: Dan Wheeler Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c | 6 ++++-- drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c index 8b63e75e5ee2..b1a916ac04e7 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn50/dcn50_hwseq.c @@ -67,7 +67,8 @@ static void dcn50_initialize_min_clocks(struct dc *dc) * audio corruption. Read current DISPCLK from DENTIST and request the same * freq to ensure that the timing is valid and unchanged. */ - clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr); + if (dc->clk_mgr->funcs->get_dispclk_from_dentist) + clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr); } clocks->ref_dtbclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dtbclk_mhz * 1000; clocks->fclk_p_state_change_support = true; @@ -644,7 +645,8 @@ void dcn50_init_hw(struct dc *dc) dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); - dcn50_initialize_min_clocks(dc); + if (dc->clk_mgr && dc->clk_mgr->funcs) + dcn50_initialize_min_clocks(dc); /* On HW init, allow idle optimizations after pipes have been turned off. * diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c index a13d36ad8a9c..ef1c10bc9bfa 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c @@ -769,7 +769,8 @@ void dcn60_init_hw(struct dc *dc) dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); - dcn401_initialize_min_clocks(dc); + if (dc->clk_mgr && dc->clk_mgr->funcs) + dcn401_initialize_min_clocks(dc); /* On HW init, allow idle optimizations after pipes have been turned off. * @@ -1150,6 +1151,7 @@ static void dcn60_build_hubbub_perfmon_sequence( /** * dcn60_update_probe_status - Set the valid flag on a latched probe result. * @status: result sink whose u was written by the GET BLS step during execute + * @probe: current probe state used to determine measurement type and validity */ static void dcn60_update_probe_status( struct dc_probe_status *status, @@ -1187,6 +1189,7 @@ static void dcn60_update_probe_status( /** * is_probe_measurement_type_for_hubbub - Returns true if the probe type is * served by the hubbub perfmon block on DCN60. + * @type: the probe measurement type to classify */ static bool is_probe_measurement_type_for_hubbub(enum dc_probe_type type) { From aee05c455ad3e72df9a0591baf0b60da518800f3 Mon Sep 17 00:00:00 2001 From: Hari Mishal Date: Tue, 15 Sep 2026 15:15:48 +0200 Subject: [PATCH 101/114] drm/amdgpu/atom: bound the VBIOS date, part number, version and build getters atom_get_vbios_date(), atom_get_vbios_pn(), atom_get_vbios_version() and atom_get_vbios_build() all walk the BIOS image using offsets and counts taken from the image itself, checked only against each other and never against ctx->bios_size: - atom_get_vbios_date() reads 14 bytes at a fixed offset that check_atom_bios()'s minimum size (0x49) does not cover. - atom_get_vbios_pn() dereferences an image-supplied u16 string offset unchecked, then walks and copies from it with only a length cap, not an image cap. - atom_get_vbios_version() takes its search bounds from image u16s; atom_find_str_in_rom() already clamps the search itself, but the match is then advanced by a fixed 18 bytes and STRLEN_NORMAL bytes are copied from there with no check that this still lands inside the image. - atom_get_vbios_build() walks from an image-supplied config-string offset toward the ROM header; a config offset at or past the header makes the pointer difference negative, wrapping through the u16 'len' and passing strscpy() a bound far larger than the 32-byte destination. Bound each getter's reads, walks and copies to ctx->bios_size, falling back to an empty string on a malformed image. Fixes: 29b4c589b43d ("drm/amdgpu: Add vbios info ioctl interface") Fixes: d6fa80266178 ("drm/amdgpu: Add vbios build number interface") Assisted-by: gkh_clanker_t1000 Signed-off-by: Hari Mishal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/atom.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c index e0e585f280e2..1a599d5cf6a5 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.c +++ b/drivers/gpu/drm/amd/amdgpu/atom.c @@ -1420,6 +1420,11 @@ static void atom_get_vbios_date(struct atom_context *ctx) p_rom = ctx->bios; + if (ctx->bios_size < OFFSET_TO_VBIOS_DATE + 14) { + ctx->date[0] = '\0'; + return; + } + date_in_rom = p_rom + OFFSET_TO_VBIOS_DATE; ctx->date[0] = '2'; @@ -1483,10 +1488,15 @@ static void atom_get_vbios_pn(struct atom_context *ctx) vbios_str = p_rom + OFFSET_TO_VBIOS_PART_NUMBER; } - if (*vbios_str == 0) { + if (vbios_str >= p_rom + ctx->bios_size) + vbios_str = NULL; + + if (vbios_str == NULL || *vbios_str == 0) { vbios_str = atom_find_str_in_rom(ctx, BIOS_ATOM_PREFIX, 3, 1024, 64); if (vbios_str == NULL) vbios_str += sizeof(BIOS_ATOM_PREFIX) - 1; + if (vbios_str >= p_rom + ctx->bios_size) + vbios_str = NULL; } OPTIMIZER_HIDE_VAR(vbios_str); if (vbios_str != NULL && *vbios_str == 0) @@ -1494,7 +1504,9 @@ static void atom_get_vbios_pn(struct atom_context *ctx) if (vbios_str != NULL) { count = 0; - while ((count < BIOS_STRING_LENGTH) && vbios_str[count] >= ' ' && + while ((count < BIOS_STRING_LENGTH) && + vbios_str + count < p_rom + ctx->bios_size && + vbios_str[count] >= ' ' && vbios_str[count] <= 'z') { ctx->vbios_pn[count] = vbios_str[count]; count++; @@ -1527,9 +1539,10 @@ static void atom_get_vbios_version(struct atom_context *ctx) /* find anchor ATOMBIOSBK-AMD */ vbios_ver = atom_find_str_in_rom(ctx, BIOS_VERSION_PREFIX, start, end, 64); - if (vbios_ver != NULL) { - /* skip ATOMBIOSBK-AMD VER */ - vbios_ver += 18; + if (vbios_ver != NULL) + vbios_ver += 18; /* skip ATOMBIOSBK-AMD VER */ + + if (vbios_ver != NULL && vbios_ver + STRLEN_NORMAL <= p_rom + ctx->bios_size) { memcpy(ctx->vbios_ver_str, vbios_ver, STRLEN_NORMAL); } else { ctx->vbios_ver_str[0] = '\0'; @@ -1545,7 +1558,13 @@ static void atom_get_vbios_build(struct atom_context *ctx) base = CU16(ATOM_ROM_TABLE_PTR); atom_rom_hdr = CSTR(base); + if ((uint32_t)base + ATOM_ROM_CFG_PTR + sizeof(uint16_t) > ctx->bios_size) + return; + str = CSTR(CU16(base + ATOM_ROM_CFG_PTR)); + if (str >= atom_rom_hdr) + return; + /* Skip config string */ while (str < atom_rom_hdr && *str++) ; From a6332b84718710f22b7392d5773675c5942faa7d Mon Sep 17 00:00:00 2001 From: Gang Ba Date: Thu, 10 Sep 2026 17:12:03 -0400 Subject: [PATCH 102/114] drm/amdkfd: Fix TCP XNACK scoreboard reset race Fix a hardware RTL bug in the TCP XNACK scoreboard reset path where a wave receiving a no-retry FAULT from UTC and subsequently terminating may leave its scoreboard entry uncleared when reset events arrive back-to-back. This can cause a later, unrelated wave using the same slot to incorrectly inherit the XNACK error state, resulting in its VMEM operations being dropped even though the wave never faulted. Signed-off-by: Gang Ba Reviewed-by: Jay Cornwall Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c | 3 +- .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c | 10 ++- .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h | 1 + drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c | 83 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h | 1 + .../drm/amd/amdkfd/kfd_device_queue_manager.c | 63 ++++++++++++++ .../drm/amd/amdkfd/kfd_device_queue_manager.h | 2 + .../gpu/drm/amd/amdkfd/kfd_int_process_v9.c | 23 +++++ .../gpu/drm/amd/include/kgd_kfd_interface.h | 1 + 9 files changed, 184 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c index 7e9f7a280c1b..98cd0435ced5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c @@ -193,5 +193,6 @@ const struct kfd2kgd_calls aldebaran_kfd2kgd = { .program_trap_handler_settings = kgd_gfx_v9_program_trap_handler_settings, .hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v9_hqd_reset, - .hqd_sdma_get_doorbell = kgd_gfx_v9_hqd_sdma_get_doorbell + .hqd_sdma_get_doorbell = kgd_gfx_v9_hqd_sdma_get_doorbell, + .hqd_gfx_clean_fault = kgd_gfx_v9_clean_fault }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c index b30ad9701bce..fc253260ade5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c @@ -37,6 +37,7 @@ #include "soc15.h" #include "soc15d.h" #include "gfx_v9_0.h" +#include "gfx_v9_4_2.h" #include "amdgpu_amdkfd_gfx_v9.h" #include @@ -1226,11 +1227,15 @@ unlock_out: uint32_t kgd_gfx_v9_hqd_sdma_get_doorbell(struct amdgpu_device *adev, int engine, int queue) - { return 0; } +void kgd_gfx_v9_clean_fault(struct amdgpu_device *adev) +{ + gfx_v9_4_2_clean_fault(adev); +} + const struct kfd2kgd_calls gfx_v9_kfd2kgd = { .program_sh_mem_settings = kgd_gfx_v9_program_sh_mem_settings, .set_pasid_vmid_mapping = kgd_gfx_v9_set_pasid_vmid_mapping, @@ -1261,5 +1266,6 @@ const struct kfd2kgd_calls gfx_v9_kfd2kgd = { .program_trap_handler_settings = kgd_gfx_v9_program_trap_handler_settings, .hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v9_hqd_reset, - .hqd_sdma_get_doorbell = kgd_gfx_v9_hqd_sdma_get_doorbell + .hqd_sdma_get_doorbell = kgd_gfx_v9_hqd_sdma_get_doorbell, + .hqd_gfx_clean_fault = kgd_gfx_v9_clean_fault }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h index 9f43f16a1ace..dba7ace872de 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h @@ -115,3 +115,4 @@ uint64_t kgd_gfx_v9_hqd_reset(struct amdgpu_device *adev, unsigned int utimeout); uint32_t kgd_gfx_v9_hqd_sdma_get_doorbell(struct amdgpu_device *adev, int engine, int queue); +void kgd_gfx_v9_clean_fault(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c index 8b84bba58752..344156bf20ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c @@ -343,6 +343,28 @@ const struct soc15_reg_entry sgpr64_init_regs_aldebaran[] = { { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE7), 0xffffffff }, }; +static const u32 fault_cleaner_compute_shader_aldebaran[] = { + 0xbe8000ff, 0x00001000, 0x80808100, 0x86000000, 0xbf85fffd, 0xbf810000 +}; + +const struct soc15_reg_entry fault_cleaner_init_regs_aldebaran[] = { + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_RESOURCE_LIMITS), 0x0000000 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_NUM_THREAD_X), 0x40 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_NUM_THREAD_Y), 1 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_NUM_THREAD_Z), 1 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_PGM_RSRC1), 0xaf0000 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_PGM_RSRC2), 0xc8 }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_PGM_RSRC3), 0xea4fac }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE0), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE1), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE2), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE3), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE4), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE5), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE6), 0xffffffff }, + { SOC15_REG_ENTRY(GC, 0, regCOMPUTE_STATIC_THREAD_MGMT_SE7), 0xffffffff }, +}; + static int gfx_v9_4_2_run_shader(struct amdgpu_device *adev, struct amdgpu_ring *ring, const u32 *shader_ptr, u32 shader_size, @@ -692,6 +714,67 @@ pro_end: return r; } +static int gfx_v9_4_2_clean_shaders(struct amdgpu_device *adev) +{ + int r; + /* CU_ID: 0~15, SIMD_ID: 0~3, WAVE_ID: 0 ~ 9 */ + int wb_size = adev->gfx.config.max_shader_engines * + CU_ID_MAX * SIMD_ID_MAX * WAVE_ID_MAX; + struct amdgpu_ib wb_ib; + struct dma_fence *fence = NULL; + u32 pattern = 0xa; + + /* bail if the compute ring is not ready */ + if (!adev->gfx.compute_ring[0].sched.ready) + return 0; + + /* allocate the write-back buffer from IB */ + memset(&wb_ib, 0, sizeof(wb_ib)); + r = amdgpu_ib_get(adev, NULL, (1 + wb_size) * sizeof(uint32_t), + AMDGPU_IB_POOL_DIRECT, &wb_ib); + if (r) { + dev_err(adev->dev, "failed to get ib (%d) for wb.\n", r); + return r; + } + memset(wb_ib.ptr, 0, (1 + wb_size) * sizeof(uint32_t)); + + r = gfx_v9_4_2_run_shader(adev, + &adev->gfx.compute_ring[0], + fault_cleaner_compute_shader_aldebaran, + sizeof(fault_cleaner_compute_shader_aldebaran), + fault_cleaner_init_regs_aldebaran, + ARRAY_SIZE(fault_cleaner_init_regs_aldebaran), + adev->gfx.cu_info.number * 32, + wb_ib.gpu_addr, pattern, &fence); + + if (r) { + dev_err(adev->dev, "failed to clear MI200\n"); + goto pro_end; + } + + /* wait for the GPU to finish processing the IB */ + r = dma_fence_wait(fence, false); + if (r) + dev_err(adev->dev, "timeout to clear MI200\n"); + +pro_end: + if (fence) + dma_fence_put(fence); + amdgpu_ib_free(&wb_ib, NULL); + + if (r) + dev_dbg(adev->dev, "Clean MI200 Failed\n"); + else + dev_dbg(adev->dev, "Clean MI200 Successfully\n"); + + return r; +} + +void gfx_v9_4_2_clean_fault(struct amdgpu_device *adev) +{ + gfx_v9_4_2_clean_shaders(adev); +} + int gfx_v9_4_2_do_edc_gpr_workarounds(struct amdgpu_device *adev) { /* only support when RAS is enabled */ diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h index a603724c1dfc..1cbade5ed657 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h @@ -30,6 +30,7 @@ void gfx_v9_4_2_init_golden_registers(struct amdgpu_device *adev, uint32_t die_id); void gfx_v9_4_2_init_sq(struct amdgpu_device *adev); void gfx_v9_4_2_set_power_brake_sequence(struct amdgpu_device *adev); +void gfx_v9_4_2_clean_fault(struct amdgpu_device *adev); int gfx_v9_4_2_do_edc_gpr_workarounds(struct amdgpu_device *adev); extern struct amdgpu_gfx_ras gfx_v9_4_2_ras; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 9da6dfe08600..ace5609fbb4b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -1506,6 +1506,67 @@ out: return retval; } +static int clean_process_queues_cpsch(struct device_queue_manager *dqm, + struct qcm_process_device *qpd) +{ + struct queue *q; + struct kfd_process_device *pdd; + int retval = 0; + + dqm_lock(dqm); + if (qpd->evicted++ > 0) /* already evicted, do nothing */ + goto out; + + pdd = qpd_to_pdd(qpd); + + /* The debugger creates processes that temporarily have not acquired + * all VMs for all devices and has no VMs itself. + * Skip queue eviction on process eviction. + */ + if (!pdd->drm_priv) + goto out; + + pr_debug_ratelimited("Evicting process pid %d queues\n", + pdd->process->lead_thread->pid); + + /* Mark all queues as evicted. Deactivate all active queues on + * the qpd. + */ + list_for_each_entry(q, &qpd->queues_list, list) { + q->properties.is_evicted = true; + if (!q->properties.is_active) + continue; + + q->properties.is_active = false; + decrement_queue_count(dqm, qpd, q); + + dqm_evict_mqd_bo(dqm, q); + } + + pdd->last_evict_timestamp = get_jiffies_64(); + + if (!down_read_trylock(&dqm->dev->adev->reset_domain->sem)) { + retval = -EIO; + goto out; + } + + retval = unmap_queues_cpsch(dqm, + KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, + 0, + USE_DEFAULT_GRACE_PERIOD, + false); + + if (!retval) { + dqm->dev->kfd2kgd->hqd_gfx_clean_fault(dqm->dev->adev); + retval = map_queues_cpsch(dqm); + } + up_read(&dqm->dev->adev->reset_domain->sem); + +out: + dqm_unlock(dqm); + return retval; +} + static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, struct qcm_process_device *qpd) { @@ -3350,6 +3411,7 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev) dqm->ops.get_queue_checkpoint_info = get_queue_checkpoint_info; dqm->ops.checkpoint_mqd = checkpoint_mqd; dqm->ops.set_perfcount = set_perfcount; + dqm->ops.clean_process_queues_cpsch = clean_process_queues_cpsch; break; case KFD_SCHED_POLICY_NO_HWS: /* initialize dqm for no cp scheduling */ @@ -3371,6 +3433,7 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev) dqm->ops.get_queue_checkpoint_info = get_queue_checkpoint_info; dqm->ops.checkpoint_mqd = checkpoint_mqd; dqm->ops.set_perfcount = set_perfcount; + dqm->ops.clean_process_queues_cpsch = clean_process_queues_cpsch; break; default: dev_err(dev->adev->dev, "Invalid scheduling policy %d\n", dqm->sched_policy); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index 21cf3c16f3f9..f55f313d6cc2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -201,6 +201,8 @@ struct device_queue_manager_ops { void *ctl_stack); void (*set_perfcount)(struct device_queue_manager *dqm, int enable); + int (*clean_process_queues_cpsch)(struct device_queue_manager *dqm, + struct qcm_process_device *qpd); }; struct device_queue_manager_asic_ops { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c index 1688d8e595f2..9b48fe598597 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c @@ -359,6 +359,24 @@ static bool event_interrupt_isr_v9(struct kfd_node *dev, !amdgpu_no_queue_eviction_on_vm_fault); } +static void gfx942_clean_fault(uint16_t pasid) +{ + struct kfd_process *p; + struct kfd_process_device *pdd = NULL; + struct device_queue_manager *dqm; + + p = kfd_lookup_process_by_pasid(pasid, &pdd); + + if (!pdd) + return; + + /* To Clean the GFX on the faulting device */ + dqm = pdd->dev->dqm; + dqm->ops.clean_process_queues_cpsch(dqm, &pdd->qpd); + + kfd_unref_process(p); +} + static void event_interrupt_wq_v9(struct kfd_node *dev, const uint32_t *ih_ring_entry) { @@ -547,6 +565,11 @@ static void event_interrupt_wq_v9(struct kfd_node *dev, return; } + /* GPR cleaner shader for MI2XX */ + if (KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 2)) + gfx942_clean_fault(pasid); + + info.vmid = vmid; info.mc_id = client_id; info.page_addr = ih_ring_entry[4] | diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 01c2631bbdff..d749669f8e6d 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -343,6 +343,7 @@ struct kfd2kgd_calls { int (*hqd_sdma_get_counter)(struct amdgpu_device *adev, void *mqd, uint32_t num_sdma_queues_per_eng, uint64_t *val); + void (*hqd_gfx_clean_fault)(struct amdgpu_device *adev); }; #endif /* KGD_KFD_INTERFACE_H_INCLUDED */ From 346d81ef808ba28465a3f19f2a0d63a758e7409c Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Tue, 15 Sep 2026 12:46:05 +0530 Subject: [PATCH 103/114] drm/amd/display: Fix redundant NULL check on dc->clk_mgr in dcn42_prepare_bandwidth dc->clk_mgr is already dereferenced inside dcn401_prepare_bandwidth(), so the dc->clk_mgr NULL check that follows is redundant and misleading. Drop it. Fixes: 53845307d52e ("drm/amd/display: Register DCN as a PMFW DF C-state client on DCN42") Reported-by: Dan Carpenter Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Ivan Lipski Cc: Harry Wentland Cc: Aurabindo Pillai Cc: Nicholas Kazlauskas Cc: Wayne Lin Cc: Dan Wheeler Cc: Leo Chen Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index ee40ee8f62ef..21c3d108abe9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -783,7 +783,7 @@ void dcn42_prepare_bandwidth( /* valid C-state watermarks have now been committed to HW, so it * is safe to vote "allow" to PMFW. */ - if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + if (dc->clk_mgr->funcs->notify_cstate_disable) dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, false); } From 4ac1835823c47903fbb278bbf474773c46f59edc Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 15 Sep 2026 12:51:24 -0500 Subject: [PATCH 104/114] drm/amdgpu: Skip KFD mapping clear before initialization amdgpu_amdkfd_clear_kfd_mapping() assumes that a non-NULL kfd_dev has a fully populated node array. This is not true when KFD device initialization fails after probe. For example, kgd2kfd_device_init() sets num_nodes before checking PCIe atomics support. On Polaris systems without the required atomics, it returns before allocating nodes[0], but the kfd_dev remains attached to the amdgpu device. A later GPU reset then dereferences nodes[0]->id. Require the authoritative KFD initialization flag before walking the node array, matching the existing KFD reset and teardown paths. Fixes: 70cadefcc616 ("drm/amdgpu: unmap all user mappings of framebuffer and doorbell before mode1 reset") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5833 Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 09feb45584ba..17365cd0a997 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -330,7 +330,7 @@ void amdgpu_amdkfd_clear_kfd_mapping(struct amdgpu_device *adev) struct kfd_dev *kfd = adev->kfd.dev; unsigned int i; - if (!kfd) + if (!kfd || !kfd->init_complete) return; for (i = 0; i < kfd->num_nodes; i++) { From 15b2b067445617f4f789f56cc566a72f88d72345 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 15 Sep 2026 12:52:41 -0500 Subject: [PATCH 105/114] drm/amdgpu: Reuse cached PCIe link device After "drm/amdgpu: Fix GPU PCIe link capability reporting" there are now a few areas of walking topology. Use the cached GPU-side link device for ASPM queries and switch state caching instead of walking the PCI topology independently in each path. Validate the internal downstream/upstream switch pair when initializing the cached link devices so all consumers share the existing topology checks. Acked-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 62 ++++++++-------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 1a2dda891c0e..68fe257689b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1353,31 +1353,6 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) #endif } -/* - * Some dGPUs expose their display endpoint below an internal PCIe switch. - * Use the switch upstream port to query the host-facing link. - */ -static struct pci_dev *amdgpu_device_get_aspm_pdev(struct amdgpu_device *adev) -{ - struct pci_dev *swds, *swus; - - swds = pci_upstream_bridge(adev->pdev); - if (!swds || - (swds->vendor != PCI_VENDOR_ID_ATI && - swds->vendor != PCI_VENDOR_ID_AMD) || - pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) - return adev->pdev; - - swus = pci_upstream_bridge(swds); - if (!swus || - (swus->vendor != PCI_VENDOR_ID_ATI && - swus->vendor != PCI_VENDOR_ID_AMD) || - pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) - return adev->pdev; - - return swus; -} - /** * amdgpu_device_should_use_aspm - check if the device should program ASPM * @@ -1413,7 +1388,7 @@ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) * the immediate upstream bridge. Use SWUS for dGPUs with an * internal switch so that this is the host-facing link. */ - aspm_pdev = amdgpu_device_get_aspm_pdev(adev); + aspm_pdev = adev->link_dev; parent = pci_upstream_bridge(aspm_pdev); if (!parent) { dev_dbg(adev->dev, "ASPM: no upstream PCIe link for %s\n", @@ -1961,15 +1936,26 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev) static void amdgpu_device_init_pcie_links(struct amdgpu_device *adev) { + struct pci_dev *swds, *swus; + adev->link_dev = adev->pdev; adev->link_partner = pci_upstream_bridge(adev->link_dev); - /* Skip upstream/downstream switches internal to the dGPU. */ - while (adev->link_partner && - adev->link_partner->vendor == PCI_VENDOR_ID_ATI) { - adev->link_dev = adev->link_partner; - adev->link_partner = pci_upstream_bridge(adev->link_dev); - } + /* Skip an upstream/downstream switch internal to the dGPU. */ + swds = adev->link_partner; + if (!swds || swds->vendor != PCI_VENDOR_ID_ATI || + pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) + return; + + swus = pci_upstream_bridge(swds); + if (!swus || + (swus->vendor != PCI_VENDOR_ID_ATI && + swus->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) + return; + + adev->link_dev = swus; + adev->link_partner = pci_upstream_bridge(swus); } /** @@ -6429,16 +6415,10 @@ static void amdgpu_device_cache_switch_state(struct amdgpu_device *adev) struct pci_dev *swus, *swds; int r; + swus = adev->link_dev != adev->pdev ? adev->link_dev : NULL; + if (!swus) + return; swds = pci_upstream_bridge(adev->pdev); - if (!swds || swds->vendor != PCI_VENDOR_ID_ATI || - pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) - return; - swus = pci_upstream_bridge(swds); - if (!swus || - (swus->vendor != PCI_VENDOR_ID_ATI && - swus->vendor != PCI_VENDOR_ID_AMD) || - pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) - return; /* If already saved, return */ if (adev->pcie_reset_ctx.swus) From 09b336c57518f363eeca7d1e2f8da041fcb613ae Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Tue, 8 Sep 2026 15:46:08 +0530 Subject: [PATCH 106/114] drm/amdgpu: Fix kfd device lock during partition The compute partition switch locked the kfd device, freed it during the pre-switch teardown, and then unlocked at the end - by which point adev->kfd.dev referenced the newly reinitialized device, so the unlock landed on the wrong object and the per-device lock was never balanced. Add a dedicated prepare function to unwind and clean up kfd device. Don't use the global lock during teardown. A new device is created after partition switch; hence there is no need to do 'unlock' on a different object after the switch. User is not supposed to run any process on the device during partition switch. Keep acquire/release semantics for init_complete so that the device remains unavailable for a process till its reinitialization is complete. Signed-off-by: Lijo Lazar Reviewed-by: Mukul Joshi Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 19 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 6 ++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c | 6 ++++-- drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c | 11 +---------- drivers/gpu/drm/amd/amdgpu/soc_v1_0.c | 11 +---------- drivers/gpu/drm/amd/amdkfd/kfd_device.c | 10 ++++++++-- drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c | 6 ++++++ 7 files changed, 45 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 17365cd0a997..9ffa0e5adec4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -245,6 +245,25 @@ void amdgpu_amdkfd_device_fini_sw(struct amdgpu_device *adev) } } +int amdgpu_amdkfd_prepare_partition_switch(struct amdgpu_device *adev) +{ + struct kfd_dev *kfd = adev->kfd.dev; + int r; + + if (!kfd) + return 0; + r = kgd2kfd_check_and_lock_kfd(kfd); + if (r) + return r; + + adev->kfd.init_complete = false; + kgd2kfd_device_fini(kfd); + adev->kfd.dev = NULL; + amdgpu_amdkfd_total_mem_size -= adev->gmc.real_vram_size; + + return 0; +} + void amdgpu_amdkfd_interrupt(struct amdgpu_device *adev, const void *ih_ring_entry) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 1b7dc0d3963b..36e222f3ac5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -170,6 +170,7 @@ void amdgpu_amdkfd_interrupt(struct amdgpu_device *adev, void amdgpu_amdkfd_device_probe(struct amdgpu_device *adev); void amdgpu_amdkfd_device_init(struct amdgpu_device *adev); void amdgpu_amdkfd_device_fini_sw(struct amdgpu_device *adev); +int amdgpu_amdkfd_prepare_partition_switch(struct amdgpu_device *adev); int amdgpu_amdkfd_check_and_lock_kfd(struct amdgpu_device *adev); void amdgpu_amdkfd_unlock_kfd(struct amdgpu_device *adev); int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev, @@ -431,6 +432,7 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf); bool kgd2kfd_device_init(struct kfd_dev *kfd, const struct kgd2kfd_shared_resources *gpu_resources); void kgd2kfd_device_exit(struct kfd_dev *kfd); +void kgd2kfd_device_fini(struct kfd_dev *kfd); void kgd2kfd_suspend(struct kfd_dev *kfd, bool suspend_proc); int kgd2kfd_resume(struct kfd_dev *kfd, bool resume_proc); void kgd2kfd_suspend_process(struct kfd_dev *kfd); @@ -485,6 +487,10 @@ static inline void kgd2kfd_device_exit(struct kfd_dev *kfd) { } +static inline void kgd2kfd_device_fini(struct kfd_dev *kfd) +{ +} + static inline void kgd2kfd_suspend(struct kfd_dev *kfd, bool suspend_proc) { } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index c80ffa08920d..e156ad5c87ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -694,14 +694,16 @@ void amdgpu_xcp_update_supported_modes(struct amdgpu_xcp_mgr *xcp_mgr) int amdgpu_xcp_pre_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags) { + int ret = 0; + /* TODO: * Stop user queues and threads, and make sure GPU is empty of work. */ if (flags & AMDGPU_XCP_OPS_KFD) - amdgpu_amdkfd_device_fini_sw(xcp_mgr->adev); + ret = amdgpu_amdkfd_prepare_partition_switch(xcp_mgr->adev); - return 0; + return ret; } int amdgpu_xcp_post_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags) diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c index 9a0c12b1a309..48b6cecf2536 100644 --- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c +++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c @@ -392,15 +392,9 @@ static int aqua_vanjaram_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, !adev->in_suspend) flags |= AMDGPU_XCP_OPS_KFD; - if (flags & AMDGPU_XCP_OPS_KFD) { - ret = amdgpu_amdkfd_check_and_lock_kfd(adev); - if (ret) - goto out; - } - ret = amdgpu_xcp_pre_partition_switch(xcp_mgr, flags); if (ret) - goto unlock; + goto out; num_xcc_per_xcp = __aqua_vanjaram_get_xcc_per_xcp(xcp_mgr, mode); if (adev->gfx.funcs->switch_partition_mode) @@ -414,9 +408,6 @@ static int aqua_vanjaram_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, ret = amdgpu_xcp_post_partition_switch(xcp_mgr, flags); if (!ret) __aqua_vanjaram_update_available_partition_mode(xcp_mgr); -unlock: - if (flags & AMDGPU_XCP_OPS_KFD) - amdgpu_amdkfd_unlock_kfd(adev); out: return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c index 757da0a30fdc..28eca48c6596 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c @@ -1097,15 +1097,9 @@ static int soc_v1_0_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, if (adev->kfd.init_complete && !amdgpu_in_reset(adev)) flags |= AMDGPU_XCP_OPS_KFD; - if (flags & AMDGPU_XCP_OPS_KFD) { - ret = amdgpu_amdkfd_check_and_lock_kfd(adev); - if (ret) - goto out; - } - ret = amdgpu_xcp_pre_partition_switch(xcp_mgr, flags); if (ret) - goto unlock; + goto out; num_xcc_per_xcp = __soc_v1_0_get_xcc_per_xcp(xcp_mgr, mode); if (adev->gfx.imu.funcs && @@ -1126,9 +1120,6 @@ static int soc_v1_0_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr, ret = amdgpu_xcp_post_partition_switch(xcp_mgr, flags); if (!ret) __soc_v1_0_update_available_partition_mode(xcp_mgr); -unlock: - if (flags & AMDGPU_XCP_OPS_KFD) - amdgpu_amdkfd_unlock_kfd(adev); out: return ret; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 1c1e7878a2a7..76b308286095 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -952,7 +952,8 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, svm_range_set_max_pages(kfd->adev); - kfd->init_complete = true; + /* Release pairs with the acquire in kfd_init_apertures(). */ + smp_store_release(&kfd->init_complete, true); dev_info(kfd_device, "added device %x:%x\n", kfd->adev->pdev->vendor, kfd->adev->pdev->device); @@ -977,7 +978,7 @@ out: return kfd->init_complete; } -void kgd2kfd_device_exit(struct kfd_dev *kfd) +void kgd2kfd_device_fini(struct kfd_dev *kfd) { if (kfd->init_complete) { /* Cleanup KFD nodes */ @@ -991,6 +992,11 @@ void kgd2kfd_device_exit(struct kfd_dev *kfd) } kfree(kfd); +} + +void kgd2kfd_device_exit(struct kfd_dev *kfd) +{ + kgd2kfd_device_fini(kfd); /* after remove a kfd device unlock kfd driver */ kgd2kfd_unlock_kfd(NULL); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c index 04c5e26f01ed..da61d939b35c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c @@ -396,6 +396,12 @@ int kfd_init_apertures(struct kfd_process *process) continue; } + /* acquire pairs with the release in kgd2kfd_device_init() */ + if (!smp_load_acquire(&dev->kfd->init_complete)) { + id++; + continue; + } + pdd = kfd_create_process_device_data(dev, process); if (!pdd) { dev_err(dev->adev->dev, From dd6f86a97260e5207d3329ad03aa89fdad61b1e6 Mon Sep 17 00:00:00 2001 From: Chengjun Yao Date: Tue, 8 Sep 2026 10:15:43 +0800 Subject: [PATCH 107/114] drm/amdgpu: fix rmmio iounmap skipped on device removal amdgpu_pci_remove() calls drm_dev_unplug() before fini_sw(), so drm_dev_enter() is already false there and the iounmap() guarded by it is skipped. This .remove path runs on both hot-unplug and plain rmmod, so the register BAR ioremap mapping leaks one instance per unload. Unmap rmmio unconditionally (guard only on non-NULL) and drop the now unused idx. Fixes: 62d5f9f7110a ("drm/amdgpu: Unmap MMIO mappings when device is not unplugged") Signed-off-by: Chengjun Yao Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 68fe257689b0..be41f70893e5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4330,7 +4330,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev) void amdgpu_device_fini_sw(struct amdgpu_device *adev) { - int i, idx; + int i; bool px; amdgpu_device_ip_fini(adev); @@ -4372,11 +4372,9 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) if ((adev->pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) vga_client_unregister(adev->pdev); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - + if (adev->rmmio) { iounmap(adev->rmmio); adev->rmmio = NULL; - drm_dev_exit(idx); } if (IS_ENABLED(CONFIG_PERF_EVENTS)) From b75fec1cdedb3ade464cf095929ba7249156b610 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Tue, 8 Sep 2026 18:18:05 +0530 Subject: [PATCH 108/114] drm/amdgpu: remove unused kfd lock wrappers amdgpu_amdkfd_check_and_lock_kfd() and amdgpu_amdkfd_unlock_kfd() no longer have any callers since the compute partition switch was reworked. Signed-off-by: Lijo Lazar Reviewed-by: Mukul Joshi Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 10 ---------- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 9ffa0e5adec4..afcfb92fb9f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -836,16 +836,6 @@ int amdgpu_amdkfd_send_close_event_drain_irq(struct amdgpu_device *adev, return 0; } -int amdgpu_amdkfd_check_and_lock_kfd(struct amdgpu_device *adev) -{ - return kgd2kfd_check_and_lock_kfd(adev->kfd.dev); -} - -void amdgpu_amdkfd_unlock_kfd(struct amdgpu_device *adev) -{ - kgd2kfd_unlock_kfd(adev->kfd.dev); -} - u64 amdgpu_amdkfd_xcp_memory_size(struct amdgpu_device *adev, int xcp_id) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 36e222f3ac5b..e3da382ceb3c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -171,8 +171,6 @@ void amdgpu_amdkfd_device_probe(struct amdgpu_device *adev); void amdgpu_amdkfd_device_init(struct amdgpu_device *adev); void amdgpu_amdkfd_device_fini_sw(struct amdgpu_device *adev); int amdgpu_amdkfd_prepare_partition_switch(struct amdgpu_device *adev); -int amdgpu_amdkfd_check_and_lock_kfd(struct amdgpu_device *adev); -void amdgpu_amdkfd_unlock_kfd(struct amdgpu_device *adev); int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev, enum kgd_engine_type engine, uint32_t vmid, uint64_t gpu_addr, From 7d3510c3d1632a56dcc05bb757a44b609749a036 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Sep 2026 19:37:44 +0300 Subject: [PATCH 109/114] drm/amd/pm: delete a stray tab This tab was accidentally added in commit 6c9e0328d1de ("drm/amd/pm: refactor PPT limits by controller and power source"). Delete it. Signed-off-by: Dan Carpenter Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index a29e76b03476..6beafabf1306 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -3077,7 +3077,7 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu, return ret; } } - return smu_v13_0_set_ppt_limit(smu, limit_type, limit); + return smu_v13_0_set_ppt_limit(smu, limit_type, limit); } else if (smu->od_enabled) { ret = smu_v13_0_set_ppt_limit(smu, limit_type, msg_limit); if (ret) From 387550e53e1405f1f960b62b22f8783db17c8e1d Mon Sep 17 00:00:00 2001 From: Francis Marlou Pacaro Date: Fri, 4 Sep 2026 08:20:21 +0800 Subject: [PATCH 110/114] drm/amd/display: fix MALL hysteresis timer underflow at high refresh rates dcn30_apply_idle_power_optimizations() derives the MALL frame cache hysteresis timer with tmr_delay = (uint32_t)(div_u64(..., denom) - 64LL); div_u64() returns a u64, so when the quotient is smaller than 64 the subtraction wraps instead of going negative and tmr_delay ends up huge. The loop that follows tries to squeeze it into the 6 bit register field by doubling denom, but that only makes the quotient smaller, so tmr_delay can never converge. tmr_scale is bumped past 3 and the function gives up with /* Delay exceeds range of hysteresis timer */ ASSERT(false); even though the requested delay is too *short* to encode, not too long. With mall_additional_timer_percent left at its default of 0, the quotient drops below 64 once the refresh rate used for the calculation goes above ~243 Hz. Every DCN 3.0 display above that loses MALL static screen entirely and splats a WARN once per boot. Reproduced on Navi 23 (RX 6600) driving 1920x1080, resetting /sys/kernel/debug/clear_warn_once between modes: refresh MALL ASSERT 144 Hz enabled no 240 Hz enabled no 280 Hz skipped yes 360 Hz skipped yes Commit 3bb68cec4db8 ("drm/amd/display: Add Overflow check to skip MALL") already covered the other end of the range, where a large stutter period makes the delay too long to encode. Cover the short end by clamping to 0, which selects the shortest hysteresis the register can express, 65.28us * 64 = ~4.18ms. That is marginally longer than what the formula asks for at these refresh rates, and erring long is the safe direction: it only delays MALL entry, it can never enter early. The numerator does not change between iterations, only denom does, so compute it once and keep both call sites inside 100 columns. The genuinely out of range case at very low refresh rates still reaches the ASSERT, which is where it belongs. Fixes: 52f2e83e2fe5 ("drm/amdgpu/display: add MALL support (v2)") Signed-off-by: Francis Marlou Pacaro Reviewed-by: Leo Li Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index 8b0a667dc59b..79f40b483caf 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -1065,10 +1065,12 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable) */ unsigned int denom = refresh_hz * 6528; unsigned int stutter_period = dc->current_state->perf_params.stutter_period_us; + uint64_t num = (1000000LL + 2 * stutter_period * refresh_hz) * + (100LL + dc->debug.mall_additional_timer_percent); + uint64_t tmr_ticks; - tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) * - (100LL + dc->debug.mall_additional_timer_percent) + denom - 1), - denom) - 64LL); + tmr_ticks = div_u64(num + denom - 1, denom); + tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0; /* In some cases the stutter period is really big (tiny modes) in these * cases MALL cant be enabled, So skip these cases to avoid a ASSERT() @@ -1090,9 +1092,8 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable) } denom *= 2; - tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) * - (100LL + dc->debug.mall_additional_timer_percent) + denom - 1), - denom) - 64LL); + tmr_ticks = div_u64(num + denom - 1, denom); + tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0; } /* Copy HW cursor */ From 061ce2b5f92214d8d867ae4ec3b24e211ff20664 Mon Sep 17 00:00:00 2001 From: Xiang Liu Date: Thu, 10 Sep 2026 22:35:03 +0800 Subject: [PATCH 111/114] drm/amd/ras: apply the MCA debug mode on every RAS resume amdgpu_ras_late_init() sends the MCA configuration to PMFW, but it runs before RAS is ready on a device that still needs a hive reset, so the request is dropped and CE polling stays disabled. A reset loses the setting too, since PMFW is reloaded and nothing programs it again. The request is a single SMU message and does not depend on the RAS block being up, so amdgpu_ras_mgr_set_debug_mode() no longer requires it. Send it from amdgpu_ras_resume(), which covers modprobe, resume from suspend, GPU reset and reset on init, and drop it from amdgpu_ras_late_init(). Signed-off-by: Xiang Liu Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 5 ++--- drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 3f3aa44921ab..6b2b8acdbf9b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -3894,6 +3894,8 @@ void amdgpu_ras_resume(struct amdgpu_device *adev) return; } + amdgpu_ras_mgr_set_debug_mode(adev, false); + if (con->flags & AMDGPU_RAS_FLAG_INIT_BY_VBIOS) { /* Set up all other IPs which are not implemented. There is a * tricky thing that IP's actual ras error type should be @@ -3954,9 +3956,6 @@ int amdgpu_ras_late_init(struct amdgpu_device *adev) if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_telemetry_en(adev)) return 0; - if (amdgpu_uniras_enabled(adev)) - amdgpu_ras_mgr_set_debug_mode(adev, false); - list_for_each_entry_safe(node, tmp, &adev->ras_list, node) { obj = node->ras_obj; if (!obj) { diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c index a02167397ce2..982ca34dba8f 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c @@ -830,8 +830,9 @@ int amdgpu_ras_mgr_set_debug_mode(struct amdgpu_device *adev, bool enable) struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev); int ret; - if (!ras_mgr || !ras_mgr->ras_core || !ras_mgr->ras_is_ready) - return false; + /* this only talks to PMFW, so it does not wait for the RAS block */ + if (!ras_mgr || !ras_mgr->ras_core) + return -EINVAL; ret = ras_core_set_debug_mode(ras_mgr->ras_core, enable); if (!ret) From b73a4958728f05e340f77fd3bd5117040be7055a Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 14 Sep 2026 15:54:24 +0100 Subject: [PATCH 112/114] drm/amdgpu: Cache the SDMA CSA address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDMA CSA address is static while the driver is loaded so lets store it in the SDMA instance allowing us to simplify and inline the amdgpu_sdma_get_csa_mc_addr() helper. Reviewed-by: Alex Deucher Reviewed-by: Timur Kristóf Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 32 +++++++++---------- drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h | 10 +++++- .../gpu/drm/amd/amdgpu/amdgpu_sdma_types.h | 2 ++ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index aa3428ed7aa6..87ecbd429f95 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -42,7 +42,9 @@ int __printf(4, 5) amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, unsigned int instance, const char *fmt, ...) { + struct amdgpu_sdma_instance *sdma; va_list args; + int r; ring->ring_obj = NULL; @@ -50,25 +52,21 @@ amdgpu_sdma_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, vsnprintf(ring->name, sizeof(ring->name), fmt, args); va_end(args); - return amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, - AMDGPU_SDMA_IRQ_INSTANCE0 + instance, - AMDGPU_RING_PRIO_DEFAULT, NULL); -} + r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq, + AMDGPU_SDMA_IRQ_INSTANCE0 + instance, + AMDGPU_RING_PRIO_DEFAULT, NULL); + if (r) + return r; -uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, - unsigned int vmid) -{ - struct amdgpu_device *adev = ring->adev; + sdma = amdgpu_sdma_get_instance_from_ring(ring); + if (amdgpu_sriov_vf(adev) || !adev->gfx.mcbp) + sdma->csa_addr = 0; + else + sdma->csa_addr = amdgpu_csa_vaddr(adev) + + AMDGPU_CSA_SDMA_OFFSET + + ring->me * AMDGPU_CSA_SDMA_SIZE; - /* don't enable OS preemption on SDMA under SRIOV */ - if (amdgpu_sriov_vf(adev) || vmid == 0 || !adev->gfx.mcbp) - return 0; - - if (ring->me > 31) - return 0; - - return amdgpu_csa_vaddr(adev) + AMDGPU_CSA_SDMA_OFFSET + - ring->me * AMDGPU_CSA_SDMA_SIZE; + return r; } int amdgpu_sdma_ras_late_init(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index ed08d521b363..51dc52757f48 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -57,7 +57,15 @@ amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring) return &ring->adev->sdma.instance[ring->me]; } -uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, unsigned vmid); +static inline uint64_t +amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, unsigned vmid) +{ + if (vmid == 0) + return 0; + else + return amdgpu_sdma_get_instance_from_ring(ring)->csa_addr; +} + int amdgpu_sdma_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block); int amdgpu_sdma_process_ras_data_cb(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h index bb0aa0c20314..1360db74d8b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma_types.h @@ -75,6 +75,8 @@ struct amdgpu_sdma_instance { uint32_t xcc_id; }; + uint64_t csa_addr; + struct amdgpu_bo *sdma_fw_obj; uint64_t sdma_fw_gpu_addr; uint32_t *sdma_fw_ptr; From 789551e08ebee7a42ddf9f98405644a1c6bd4bdb Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 15 Sep 2026 12:49:33 +0100 Subject: [PATCH 113/114] drm/amdgpu: Extend logical to device instance lookup to all devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A subset of supported devices can have a mismatch between the logical and device engine instance numbers which the driver currently solves by a combination of lookup table generated at driver init time, and a conditionally set vfunc checked and used at runtime to do the lookup. As the requirement to do these lookups is relatively widespread in the code base, plus the fact the mapping table is already allocated for all possible hw ips and instances, we can significantly streamline them by initializing the said lookup table unconditionally to the predominant "identity map" (logical instance numbers equal to device instances), and allow affected parts to re-configure the table based on their configuration. The effect is that for each lookup site we replace the conditional followed by a indirect function call with a simple two-dimensional table lookup. Bloat-o-meter agrees the savings are significant: add/remove: 13/12 grow/shrink: 23/279 up/down: 51626/-198973 (-147347) ... Total: Before=10139692, After=9992345, chg -1.45% Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c | 1 + .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c | 67 ++++--------------- drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h | 20 ++++-- drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c | 1 + drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c | 30 ++++++++- drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c | 8 +-- drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 1 + drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c | 1 + drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c | 1 + drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 1 + drivers/gpu/drm/amd/amdgpu/soc15_common.h | 6 +- drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c | 1 + drivers/gpu/drm/amd/amdkfd/kfd_device.c | 1 + .../drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c | 1 + .../drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c | 1 + drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h | 7 +- 34 files changed, 92 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c index bc079b95fc52..4626e0b13909 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c @@ -20,6 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_amdkfd.h" #include "amdgpu_amdkfd_gfx_v9.h" #include "amdgpu_amdkfd_aldebaran.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c index fc253260ade5..0d62cf475f40 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c @@ -20,6 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_amdkfd.h" #include "gc/gc_9_0_offset.h" #include "gc/gc_9_0_sh_mask.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index be41f70893e5..dcf72ff43888 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -68,6 +68,7 @@ #include "amdgpu_vf_error.h" #include "amdgpu_amdkfd.h" +#include "amdgpu_ip.h" #include "amdgpu_pm.h" #include "amdgpu_xgmi.h" @@ -3834,6 +3835,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, spin_lock_init(&adev->irq.lock); + amdgpu_ip_map_init(adev); amdgpu_early_init_rlc_reg_funcs(adev); amdgpu_device_init_apu_flags(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c index a426327f2141..734f2a0bc79a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c @@ -24,39 +24,16 @@ #include "amdgpu.h" #include "amdgpu_ip.h" -static int8_t amdgpu_logical_to_dev_inst(struct amdgpu_device *adev, - enum amd_hw_ip_block_type block, - int8_t inst) +u32 amdgpu_ip_map_logical_to_dev_mask(struct amdgpu_ip_map_info *ip_map, + enum amd_hw_ip_block_type block, + u32 mask) { - int8_t dev_inst; - - switch (block) { - case GC_HWIP: - case SDMA0_HWIP: - /* Both JPEG and VCN as JPEG is only alias of VCN */ - case VCN_HWIP: - dev_inst = adev->ip_map.dev_inst[block][inst]; - break; - default: - /* For rest of the IPs, no look up required. - * Assume 'logical instance == physical instance' for all configs. */ - dev_inst = inst; - break; - } - - return dev_inst; -} - -static uint32_t amdgpu_logical_to_dev_mask(struct amdgpu_device *adev, - enum amd_hw_ip_block_type block, - uint32_t mask) -{ - uint32_t dev_mask = 0; - int8_t log_inst, dev_inst; + unsigned int log_inst, dev_inst; + u32 dev_mask = 0; while (mask) { log_inst = ffs(mask) - 1; - dev_inst = amdgpu_logical_to_dev_inst(adev, block, log_inst); + dev_inst = ip_map->dev_inst[block][log_inst]; dev_mask |= (1 << dev_inst); mask &= ~(1 << log_inst); } @@ -64,35 +41,15 @@ static uint32_t amdgpu_logical_to_dev_mask(struct amdgpu_device *adev, return dev_mask; } -static void amdgpu_populate_ip_map(struct amdgpu_device *adev, - enum amd_hw_ip_block_type ip_block, - uint32_t inst_mask) -{ - int l = 0, i; - - while (inst_mask) { - i = ffs(inst_mask) - 1; - adev->ip_map.dev_inst[ip_block][l++] = i; - inst_mask &= ~(1 << i); - } - for (; l < HWIP_MAX_INSTANCE; l++) - adev->ip_map.dev_inst[ip_block][l] = -1; -} - void amdgpu_ip_map_init(struct amdgpu_device *adev) { - u32 ip_map[][2] = { - { GC_HWIP, adev->gfx.xcc_mask }, - { SDMA0_HWIP, adev->sdma.sdma_mask }, - { VCN_HWIP, adev->vcn.inst_mask }, - }; - int i; + int i, j; - for (i = 0; i < ARRAY_SIZE(ip_map); ++i) - amdgpu_populate_ip_map(adev, ip_map[i][0], ip_map[i][1]); - - adev->ip_map.logical_to_dev_inst = amdgpu_logical_to_dev_inst; - adev->ip_map.logical_to_dev_mask = amdgpu_logical_to_dev_mask; + /* Assume 'logical instance == physical instance' unless overridden. */ + for (i = 0; i < MAX_HWIP; i++) { + for (j = 0; j < HWIP_MAX_INSTANCE; j++) + adev->ip_map.dev_inst[i][j] = j; + } } int amdgpu_ip_block_suspend(struct amdgpu_ip_block *ip_block) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h index 70fc4e5db51f..5dd6273982c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h @@ -90,13 +90,7 @@ struct amdgpu_fence; struct amdgpu_ip_map_info { /* Map of logical to actual dev instances/mask */ - uint32_t dev_inst[MAX_HWIP][HWIP_MAX_INSTANCE]; - int8_t (*logical_to_dev_inst)(struct amdgpu_device *adev, - enum amd_hw_ip_block_type block, - int8_t inst); - uint32_t (*logical_to_dev_mask)(struct amdgpu_device *adev, - enum amd_hw_ip_block_type block, - uint32_t mask); + unsigned int dev_inst[MAX_HWIP][HWIP_MAX_INSTANCE]; }; #define AMDGPU_MAX_IP_NUM AMD_IP_BLOCK_TYPE_NUM @@ -125,6 +119,18 @@ struct amdgpu_ip_block { void amdgpu_ip_map_init(struct amdgpu_device *adev); +u32 amdgpu_ip_map_logical_to_dev_mask(struct amdgpu_ip_map_info *ip_map, + enum amd_hw_ip_block_type block, + u32 mask); + +static inline unsigned int +amdgpu_ip_map_logical_to_dev_inst(struct amdgpu_ip_map_info *ip_map, + enum amd_hw_ip_block_type block, + unsigned int inst) +{ + return ip_map->dev_inst[block][inst]; +} + int amdgpu_ip_block_suspend(struct amdgpu_ip_block *ip_block); int amdgpu_ip_block_resume(struct amdgpu_ip_block *ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 208566ffe898..9887a4f99aed 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -25,6 +25,7 @@ */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_jpeg.h" #include "amdgpu_pm.h" #include "soc15d.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index a676fccc466d..66eb2f34cc0d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -27,6 +27,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_psp.h" #include "amdgpu_ucode.h" #include "amdgpu_xgmi.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index e156ad5c87ab..ab14079d1c42 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -23,6 +23,7 @@ #include "amdgpu.h" #include "amdgpu_xcp.h" #include "amdgpu_drv.h" +#include "amdgpu_ip.h" #include #include "../amdxcp/amdgpu_xcp_drv.h" diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c index 48b6cecf2536..05e4edf72e4d 100644 --- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c +++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c @@ -505,6 +505,34 @@ static int aqua_vanjaram_xcp_mgr_init(struct amdgpu_device *adev) return ret; } +static void amdgpu_populate_ip_map(struct amdgpu_device *adev, + enum amd_hw_ip_block_type ip_block, + uint32_t inst_mask) +{ + int l = 0, i; + + while (inst_mask) { + i = ffs(inst_mask) - 1; + adev->ip_map.dev_inst[ip_block][l++] = i; + inst_mask &= ~(1 << i); + } + for (; l < HWIP_MAX_INSTANCE; l++) + adev->ip_map.dev_inst[ip_block][l] = -1; +} + +static void amdgpu_ip_map_aqua_vanjaram_override(struct amdgpu_device *adev) +{ + u32 ip_map[][2] = { + { GC_HWIP, adev->gfx.xcc_mask }, + { SDMA0_HWIP, adev->sdma.sdma_mask }, + { VCN_HWIP, adev->vcn.inst_mask }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ip_map); ++i) + amdgpu_populate_ip_map(adev, ip_map[i][0], ip_map[i][1]); +} + int aqua_vanjaram_init_soc_config(struct amdgpu_device *adev) { u32 mask, avail_inst, inst_mask = adev->sdma.sdma_mask; @@ -539,7 +567,7 @@ int aqua_vanjaram_init_soc_config(struct amdgpu_device *adev) if (ret) return ret; - amdgpu_ip_map_init(adev); + amdgpu_ip_map_aqua_vanjaram_override(adev); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index ce48da12edb2..484ecde55ac9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_gfx.h" #include "soc15.h" #include "soc15d.h" @@ -1646,10 +1647,9 @@ static bool gfx_v9_4_3_check_rlcg_range(struct amdgpu_device *adev, const struct soc15_reg_rlcg *entry; entry = &entries[i]; - inst = adev->ip_map.logical_to_dev_inst ? - adev->ip_map.logical_to_dev_inst( - adev, entry->hwip, entry->instance) : - entry->instance; + inst = amdgpu_ip_map_logical_to_dev_inst(&adev->ip_map, + entry->hwip, + entry->instance); reg = adev->reg_offset[entry->hwip][inst][entry->segment] + entry->reg; if (offset == reg) diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c index 8397d3d38287..e89813590539 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c @@ -21,6 +21,7 @@ * */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_xcp.h" #include "gfxhub_v1_2.h" #include "gfxhub_v1_1.h" diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index 9a8a7921ce98..490b4c4eb662 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -26,6 +26,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_atomfirmware.h" #include "gmc_v10_0.h" #include "umc_v8_7.h" diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index bee269a8877a..3cdc7893587a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -26,6 +26,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_atomfirmware.h" #include "gmc_v11_0.h" #include "umc_v8_10.h" diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index b6cd6babb035..d7f56bbcfd44 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -27,6 +27,7 @@ #include "amdgpu.h" #include "amdgpu_atomfirmware.h" +#include "amdgpu_ip.h" #include "gmc_v12_0.h" #include "gmc_v12_1.h" #include "athub/athub_4_1_0_sh_mask.h" diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 633772fa71fd..5245d8850a42 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -63,6 +63,7 @@ #include "ivsrcid/vmc/irqsrcs_vmc_1_0.h" +#include "amdgpu_ip.h" #include "amdgpu_ras.h" #include "amdgpu_xgmi.h" diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 49f84e6647a5..0343a64595f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -22,6 +22,7 @@ */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_jpeg.h" #include "soc15.h" #include "soc15d.h" diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c index 197d1af18a3a..4da3f9c995c1 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -22,6 +22,7 @@ */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_jpeg.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c index 9b57fdc1e69a..dcdb089b4308 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c @@ -22,6 +22,7 @@ */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_jpeg.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c index f67868132bed..f2debf6b6bd0 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -22,6 +22,7 @@ */ #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_jpeg.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c index def02993b7cf..ad24441776d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c @@ -22,6 +22,7 @@ */ #include "amdgpu.h" #include "nbio_v7_9.h" +#include "amdgpu_ip.h" #include "amdgpu_ras.h" #include "nbio/nbio_7_9_0_offset.h" diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index ebf3a9a454c9..fbae33f2724e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -27,6 +27,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_xcp.h" #include "amdgpu_ucode.h" #include "amdgpu_trace.h" diff --git a/drivers/gpu/drm/amd/amdgpu/soc15_common.h b/drivers/gpu/drm/amd/amdgpu/soc15_common.h index 47e0329b6f3f..937d02ef7124 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15_common.h +++ b/drivers/gpu/drm/amd/amdgpu/soc15_common.h @@ -26,11 +26,9 @@ /* GET_INST returns the physical instance corresponding to a logical instance */ #define GET_INST(ip, inst) \ - (adev->ip_map.logical_to_dev_inst ? \ - adev->ip_map.logical_to_dev_inst(adev, ip##_HWIP, inst) : inst) + amdgpu_ip_map_logical_to_dev_inst(&adev->ip_map, ip##_HWIP, inst) #define GET_MASK(ip, mask) \ - (adev->ip_map.logical_to_dev_mask ? \ - adev->ip_map.logical_to_dev_mask(adev, ip##_HWIP, mask) : mask) + amdgpu_ip_map_logical_to_dev_mask(&adev->ip_map, ip##_HWIP, mask) /* Register Access Macros */ #define SOC15_REG_OFFSET(ip, inst, reg) (adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c index c559213e442c..7d2b9e4b913a 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c @@ -25,6 +25,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "soc15.h" #include "soc15d.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c index 732aaa1d8e67..10ef5616b2f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c @@ -25,6 +25,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index e876733b754a..0f4f4d66e2e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "amdgpu_cs.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 60a5636c0073..4b7f7bbe18da 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "amdgpu_cs.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c index 3d28289bc4f1..2fa8f0d911d0 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c @@ -25,6 +25,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c index 660dab262dfb..f30f80c1b07f 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "amdgpu_cs.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index 08fca20b2c99..7b7c3bc9f046 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c index b3b83ad20103..09e553b8a657 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c @@ -23,6 +23,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "soc15.h" diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 76b308286095..23db8dcb076d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -34,6 +34,7 @@ #include "kfd_svm.h" #include "kfd_migrate.h" #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_xcp.h" #define MQD_SIZE_ALIGNED 768 diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index 0033453f27e5..1ecf65d402ba 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -25,6 +25,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_smu.h" #include "smu_v13_0_12_pmfw.h" #include "smu_v13_0_6_ppt.h" diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 19bc6ce29da0..ee380488d212 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -25,6 +25,7 @@ #include #include "amdgpu.h" +#include "amdgpu_ip.h" #include "amdgpu_smu.h" #include "atomfirmware.h" #include "amdgpu_atomfirmware.h" diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h index de3da5571ef8..1c5e02de7fca 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h +++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h @@ -31,6 +31,7 @@ #include #include #include "amdgpu.h" +#include "amdgpu_ip.h" /* inject address is 52 bits */ #define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52) @@ -96,15 +97,13 @@ #define RAS_GET_INST(dev, ip, inst) \ ({ \ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \ - adev->ip_map.logical_to_dev_inst ? \ - adev->ip_map.logical_to_dev_inst(adev, ip##_HWIP, inst) : inst; \ + amdgpu_ip_map_logical_to_dev_inst(&adev->ip_map, ip##_HWIP, inst); \ }) #define RAS_GET_MASK(dev, ip, mask) \ ({ \ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \ - (adev->ip_map.logical_to_dev_mask ? \ - adev->ip_map.logical_to_dev_mask(adev, ip##_HWIP, mask) : mask); \ + amdgpu_ip_map_logical_to_dev_mask(&adev->ip_map, ip##_HWIP, mask); \ }) static inline void *ras_radix_tree_delete_iter(struct radix_tree_root *root, void *iter) From cf48bd0a46279f4607956cb17c71bda96ee802e3 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 14 Sep 2026 15:54:26 +0100 Subject: [PATCH 114/114] drm/amdgpu: Use memset32 for SDMA padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of open coding it via the inefficient amdgpu_ring_write(), which which the compiler is not able to optimise much, we can add a new amdgpu_ring_fill() helper which pads using memset32. We do this by moving the existing implementation from amdgpu_ring_insert_nop(), which is used by the GFX rings, and then individually convert all the SDMA insert_nop implementations. For SDMA this should have a lesser benefit than for GFX (only SDMA v4.0 uses the 256 byte ring padding while the rest use 16), but it does not harm to be consistent. Reviewed-by: Alex Deucher Reviewed-by: Timur Kristóf Signed-off-by: Tvrtko Ursulin Cc: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c | 17 +--------------- drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h | 26 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 15 +++++++------- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 15 +++++++------- 11 files changed, 90 insertions(+), 88 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 132ca5ce3a6d..3aea81c75fdd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -117,22 +117,7 @@ int amdgpu_ring_alloc(struct amdgpu_ring *ring, unsigned int ndw) */ void amdgpu_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { - uint32_t occupied, chunk1, chunk2; - - occupied = ring->wptr & ring->buf_mask; - chunk1 = ring->buf_mask + 1 - occupied; - chunk1 = (chunk1 >= count) ? count : chunk1; - chunk2 = count - chunk1; - - if (chunk1) - memset32(&ring->ring[occupied], ring->funcs->nop, chunk1); - - if (chunk2) - memset32(ring->ring, ring->funcs->nop, chunk2); - - ring->wptr += count; - ring->wptr &= ring->ptr_mask; - ring->count_dw -= count; + amdgpu_ring_fill(ring, ring->funcs->nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 49df2c4745a1..b2040f80b7f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -539,6 +539,32 @@ static inline void amdgpu_ring_write_multiple(struct amdgpu_ring *ring, ring->count_dw -= count_dw; } +static inline void amdgpu_ring_fill(struct amdgpu_ring *ring, + u32 val, u32 count) +{ + const u32 buf_mask = ring->buf_mask; + u32 occupied, chunk1, chunk2; + u64 wptr = ring->wptr; + + if (count == 0) + return; + + occupied = wptr & buf_mask; + chunk1 = buf_mask + 1 - occupied; + chunk1 = (chunk1 >= count) ? count : chunk1; + chunk2 = count - chunk1; + + if (chunk1) + memset32(&ring->ring[occupied], val, chunk1); + + if (chunk2) + memset32(ring->ring, val, chunk2); + + wptr += count; + ring->wptr = wptr & ring->ptr_mask; + ring->count_dw -= count; +} + static inline unsigned int amdgpu_ring_get_dw_distance(struct amdgpu_ring *ring, u64 start_wptr, u64 end_wptr) { diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 95613013e07c..6b64cf83ee5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -224,15 +224,14 @@ static void sdma_v2_4_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v2_4_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 835c0278fd87..6f7243370acb 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -401,15 +401,14 @@ static void sdma_v3_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v3_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index dc75755abae6..902d31300326 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -784,15 +784,14 @@ static void sdma_v4_0_page_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v4_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index fbae33f2724e..df788361e865 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -350,15 +350,14 @@ static void sdma_v4_4_2_page_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v4_4_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index a8d27f1944a2..c6349f0e632f 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -407,15 +407,14 @@ static void sdma_v5_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v5_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index 39660b36b998..683f27c27665 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -255,15 +255,14 @@ static void sdma_v5_2_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v5_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index e940401f814e..16649dba6329 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -243,15 +243,14 @@ static void sdma_v6_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v6_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /* diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index 3dee4d17eef3..edd9c437f7cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -245,15 +245,14 @@ static void sdma_v7_0_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v7_0_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index 934e84cb81e2..321c67e01443 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -239,15 +239,14 @@ static void sdma_v7_1_ring_set_wptr(struct amdgpu_ring *ring) static void sdma_v7_1_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count) { struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring); - const bool burst_nop = sdma->burst_nop; - int i; + const u32 nop = ring->funcs->nop; - for (i = 0; i < count; i++) - if (i == 0 && burst_nop) - amdgpu_ring_write(ring, ring->funcs->nop | - SDMA_PKT_NOP_HEADER_COUNT(count - 1)); - else - amdgpu_ring_write(ring, ring->funcs->nop); + if (count && sdma->burst_nop) { + --count; + amdgpu_ring_write(ring, nop | SDMA_PKT_NOP_HEADER_COUNT(count)); + } + + amdgpu_ring_fill(ring, nop, count); } /**