summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWenjing Liu <wenjing.liu@amd.com>2026-08-05 16:41:35 -0400
committerAlex Deucher <alexander.deucher@amd.com>2026-09-10 12:01:24 -0400
commit11f8fe9480ef4546cb753f65ab9743302008f692 (patch)
treedfb6c9fd0705e0140092fd1486449614cd6aebc1
parent9344d7185b6738b40c20f9d47b951506ab5b68c7 (diff)
downloadlinux-next-11f8fe9480ef4546cb753f65ab9743302008f692.tar.gz
linux-next-11f8fe9480ef4546cb753f65ab9743302008f692.zip
drm/amd/display: Add stressed peak bandwidth probe with DMA contention
[Why] Peak-bandwidth-under-contention validation currently needs an external tool to manufacture memory contention while querying peak bandwidth. Expose this as an explicit probe variant so the driver can generate that contention itself. [How] Add a new probe type that builds the same gated perfmon sequence as the plain peak-BW probe, with a DMA copy of the current surface into a scratch buffer inserted before the measurement window to synthesize a competing memory client. Move the scratch buffer fields onto the base resource pool struct instead of an ASIC-specific subclass, and reject the probe with a new status code when the buffer was never allocated. Also fixes a stack- corruption bug where a local variable's address was captured by a deferred callback and used after the local went out of scope, and a missing NULL check on a similar output parameter used by an early-return path. Reviewed-by: Alvin Lee <alvin.lee2@amd.com> Signed-off-by: Wenjing Liu <wenjing.liu@amd.com> Signed-off-by: Ray Wu <ray.wu@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_debug.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c29
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_probe.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c26
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h14
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_status.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_types.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c19
11 files changed, 106 insertions, 4 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
index 2933b660e3d7..f3348e0cb1d6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
@@ -274,6 +274,8 @@ char *dc_status_to_str(enum dc_status status)
return "HW Cursor not supported";
case DC_FAIL_DP_TUNNEL_BW_VALIDATE:
return "Fail DP Tunnel BW validation";
+ case DC_NO_DRAM_BUFFER_RESOURCE:
+ return "No DRAM buffer resource";
case DC_ERROR_UNEXPECTED:
return "Unexpected error";
default:
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 de1772f75293..b54466e4f42b 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
@@ -1797,6 +1797,9 @@ void hwss_execute_sequence(struct dc *dc,
case DMUB_SEND_DMCUB_CMD:
hwss_send_dmcub_cmd(params);
break;
+ case LSDMA_SEND_PIO_COPY:
+ hwss_lsdma_send_pio_copy(params);
+ break;
case DMUB_SUBVP_SAVE_SURF_ADDR:
hwss_subvp_save_surf_addr(params);
break;
@@ -2670,6 +2673,17 @@ void hwss_send_dmcub_cmd(union block_sequence_params *params)
dc_wake_and_execute_dmub_cmd(ctx, cmd, wait_type);
}
+void hwss_lsdma_send_pio_copy(union block_sequence_params *params)
+{
+ struct dc_dmub_srv *dc_dmub_srv = params->lsdma_send_pio_copy_params.dc_dmub_srv;
+ uint64_t src_addr = params->lsdma_send_pio_copy_params.src_addr;
+ uint64_t dst_addr = params->lsdma_send_pio_copy_params.dst_addr;
+ uint32_t byte_count = params->lsdma_send_pio_copy_params.byte_count;
+ uint32_t overlap_disable = params->lsdma_send_pio_copy_params.overlap_disable;
+
+ dmub_lsdma_send_pio_copy_command(dc_dmub_srv, src_addr, dst_addr, byte_count, overlap_disable);
+}
+
/*
* Helper function to add TG program global sync to block sequence
*/
@@ -5214,6 +5228,21 @@ void hwss_add_hubbub_perfmon_arm_out_of_order_bw(struct block_sequence_state *se
}
}
+void hwss_add_lsdma_send_pio_copy(struct block_sequence_state *seq_state,
+ struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t dst_addr,
+ uint32_t byte_count, uint32_t overlap_disable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = LSDMA_SEND_PIO_COPY;
+ seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.dc_dmub_srv = dc_dmub_srv;
+ seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.src_addr = src_addr;
+ seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.dst_addr = dst_addr;
+ seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.byte_count = byte_count;
+ seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.overlap_disable = overlap_disable;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_add_hubbub_perfmon_start_out_of_order_bw(struct block_sequence_state *seq_state,
struct hubbub *hubbub)
{
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 25443ceff158..f9b8e9474b4c 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4475,6 +4475,10 @@ enum dc_status resource_validate_probe_set(struct dc *dc,
if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL)
return DC_NOT_SUPPORTED;
+
+ if (probes[i].type == DC_PROBE_PEAK_MEM_BW_STRESSED &&
+ !dc->res_pool->lsdma_scratch.buffer)
+ return DC_NO_DRAM_BUFFER_RESOURCE;
}
return DC_OK;
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index e1f1b0602b6d..91db7bce7eea 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -584,6 +584,7 @@ struct dc_config {
bool forced_clocks;
union allow_lttpr_non_transparent_mode allow_lttpr_non_transparent_mode;
bool multi_mon_pp_mclk_switch;
+ bool lsdma_peak_bw_contention_support;
bool disable_dmcu;
bool allow_4to1MPC;
bool enable_windowed_mpo_odm;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_probe.h b/drivers/gpu/drm/amd/display/dc/dc_probe.h
index ebf33b162b63..52bffbebf7ad 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_probe.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_probe.h
@@ -20,6 +20,7 @@ enum dc_probe_type {
DC_PROBE_URGENT_RAMP_LATENCY,
DC_PROBE_URGENT_ASSERTION_COUNT,
DC_PROBE_PREFETCH_DATA_SIZE,
+ DC_PROBE_PEAK_MEM_BW_STRESSED, /* peak BW under synthetic memory contention */
};
/**
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 f1e6b2a1000d..0169ffcd9343 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
@@ -1713,7 +1713,8 @@ static uint32_t hubbub60_perfmon_get_in_order_bandwidth_mbps(
return 0;
measuring_duration_ns = count4 * 1000 / refclk_mhz;
- *duration_ns = measuring_duration_ns;
+ if (duration_ns)
+ *duration_ns = measuring_duration_ns;
if (min_duration_ns > measuring_duration_ns)
return 0;
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 8ee9791b2c46..0e6526e92544 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
@@ -893,7 +893,6 @@ 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 };
- uint32_t duration_ns = 0;
if (!hubbub || !hubbub->funcs || !hubbub->funcs->perfmon.reset)
return;
@@ -910,6 +909,7 @@ static void dcn60_build_hubbub_perfmon_sequence(
switch (probe->type) {
case DC_PROBE_PEAK_MEM_BW:
+ case DC_PROBE_PEAK_MEM_BW_STRESSED:
/* Start at the vblank edge and stop at the next vactive so the counter
* spans exactly one prefetch window, capturing prefetch traffic only. */
if (!hubbub->funcs->perfmon.arm_measuring_out_of_order_bandwidth ||
@@ -919,12 +919,30 @@ static void dcn60_build_hubbub_perfmon_sequence(
hwss_add_hubbub_perfmon_reset(&seq_state, hubbub);
hwss_add_hubbub_perfmon_arm_out_of_order_bw(&seq_state, hubbub);
+
+ if (probe->type == DC_PROBE_PEAK_MEM_BW_STRESSED) {
+ struct dc_plane_state *plane = (context->stream_status[0].plane_count > 0) ?
+ context->stream_status[0].plane_states[0] : NULL;
+
+ if (dc->res_pool->lsdma_scratch.buffer && plane) {
+ unsigned int surface_bytes = plane->plane_size.surface_pitch *
+ plane->plane_size.surface_size.height;
+ unsigned int copy_bytes = (surface_bytes < dc->res_pool->lsdma_scratch.size) ?
+ surface_bytes : dc->res_pool->lsdma_scratch.size;
+
+ hwss_add_lsdma_send_pio_copy(&seq_state, dc->ctx->dmub_srv,
+ (uint64_t)plane->address.grph.addr.quad_part,
+ (uint64_t)dc->res_pool->lsdma_scratch.pa,
+ copy_bytes, /* overlap_disable */ 1);
+ }
+ }
+
hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE);
hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VBLANK);
hwss_add_hubbub_perfmon_start_out_of_order_bw(&seq_state, hubbub);
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, &duration_ns);
+ refclk_mhz, &status->u.bandwidth_mbps, NULL);
break;
case DC_PROBE_AVG_MEM_BW:
@@ -941,7 +959,7 @@ static void dcn60_build_hubbub_perfmon_sequence(
hwss_add_tg_wait_for_state(&seq_state, ref_tg, CRTC_STATE_VACTIVE);
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, &duration_ns);
+ refclk_mhz, 0, &status->u.bandwidth_mbps, NULL);
break;
case DC_PROBE_MEM_LATENCY:
@@ -1006,6 +1024,7 @@ static void dcn60_update_probe_status(struct dc_probe_status *status)
{
switch (status->type) {
case DC_PROBE_PEAK_MEM_BW:
+ case DC_PROBE_PEAK_MEM_BW_STRESSED:
case DC_PROBE_AVG_MEM_BW:
/* Zero bandwidth means the counter did not fire — treat as invalid. */
status->valid = (status->u.bandwidth_mbps != 0);
@@ -1029,6 +1048,7 @@ static bool is_probe_measurement_type_for_hubbub(enum dc_probe_type type)
{
switch (type) {
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_URGENT_ASSERTION_COUNT:
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 3f9eda74fb1b..f75e34a09dca 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -142,6 +142,14 @@ struct send_dmcub_cmd_params {
enum dm_dmub_wait_type wait_type;
};
+struct lsdma_send_pio_copy_params {
+ struct dc_dmub_srv *dc_dmub_srv;
+ uint64_t src_addr;
+ uint64_t dst_addr;
+ uint32_t byte_count;
+ uint32_t overlap_disable;
+};
+
struct setup_dpp_params {
struct pipe_ctx *pipe_ctx;
};
@@ -1058,6 +1066,7 @@ union block_sequence_params {
struct update_info_frame_params update_info_frame_params;
struct program_manual_trigger_params program_manual_trigger_params;
struct send_dmcub_cmd_params send_dmcub_cmd_params;
+ struct lsdma_send_pio_copy_params lsdma_send_pio_copy_params;
struct setup_dpp_params setup_dpp_params;
struct program_bias_and_scale_params program_bias_and_scale_params;
struct set_output_transfer_func_params set_output_transfer_func_params;
@@ -1236,6 +1245,7 @@ enum block_sequence_func {
HUBP_SET_DMDATA_ATTRIBUTES,
OPTC_PROGRAM_MANUAL_TRIGGER,
DMUB_SEND_DMCUB_CMD,
+ LSDMA_SEND_PIO_COPY,
DPP_SETUP_DPP,
DPP_PROGRAM_BIAS_AND_SCALE,
DPP_SET_OUTPUT_TRANSFER_FUNC,
@@ -1847,6 +1857,7 @@ void hwss_process_outstanding_hw_updates(struct dc *dc,
struct dc_state *dc_context);
void hwss_send_dmcub_cmd(union block_sequence_params *params);
+void hwss_lsdma_send_pio_copy(union block_sequence_params *params);
void hwss_program_manual_trigger(union block_sequence_params *params);
@@ -2205,6 +2216,9 @@ void hwss_add_dmub_send_dmcub_cmd(struct block_sequence_state *seq_state,
void hwss_add_dmub_subvp_save_surf_addr(struct block_sequence_state *seq_state,
struct dc_dmub_srv *dc_dmub_srv, struct dc_plane_address *addr, uint8_t subvp_index);
+void hwss_add_lsdma_send_pio_copy(struct block_sequence_state *seq_state,
+ struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t dst_addr,
+ uint32_t byte_count, uint32_t overlap_disable);
void hwss_add_hubp_wait_pipe_read_start(struct block_sequence_state *seq_state,
struct hubp *hubp);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_status.h b/drivers/gpu/drm/amd/display/dc/inc/core_status.h
index 1a17e727ed04..69215d0e8f46 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_status.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h
@@ -72,6 +72,8 @@ enum dc_status {
/// Handshake failed, programming aborted, DCN may be in inconsistent state.
DC_DPMS_FAILED_INCOMPLETE = 33,
+ DC_NO_DRAM_BUFFER_RESOURCE = 34,
+
DC_ERROR_UNEXPECTED = -1
};
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 b18d607fb222..d2cca2c97a41 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
@@ -247,6 +247,8 @@ struct audio_support{
#define NO_UNDERLAY_PIPE -1
struct resource_pool {
+ struct dc_context *ctx;
+
struct mem_input *mis[MAX_PIPES];
struct hubp *hubps[MAX_PIPES];
struct input_pixel_processor *ipps[MAX_PIPES];
@@ -264,6 +266,13 @@ struct resource_pool {
struct dce_i2c_sw *sw_i2cs[MAX_PIPES];
bool i2c_hw_buffer_in_use;
+ /* LSDMA scratch memory. buffer is NULL when not allocated. */
+ struct {
+ void *buffer;
+ long long pa;
+ unsigned int size;
+ } lsdma_scratch;
+
struct dwbc *dwbc[MAX_DWB_PIPES];
struct mcif_wb *mcif_wb[MAX_DWB_PIPES];
struct {
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 8228fb62a350..5ff5462d5de1 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
@@ -3,6 +3,7 @@
// Copyright 2024 Advanced Micro Devices, Inc.
#include "dm_services.h"
+#include "dm_helpers.h"
#include "dc.h"
#include "dcn32/dcn32_init.h"
@@ -84,6 +85,8 @@
#include "dml2_wrapper/dml2_wrapper.h"
#include "dml2_wrapper/dml21_wrapper/dml21_wrapper.h"
+#define LSDMA_CONTENTION_BUFFER_SIZE (64 * 1024 * 1024)
+
#define DC_LOGGER_INIT(logger)
/* begin *********************
@@ -1626,6 +1629,12 @@ static void dcn60_resource_destruct(struct dcn60_resource_pool *pool)
{
unsigned int i;
+ if (pool->base.lsdma_scratch.buffer) {
+ dm_helpers_free_gpu_mem(pool->base.ctx,
+ DC_MEM_ALLOC_TYPE_GART, pool->base.lsdma_scratch.buffer);
+ pool->base.lsdma_scratch.buffer = NULL;
+ }
+
for (i = 0; i < pool->base.stream_enc_count; i++) {
if (pool->base.stream_enc[i] != NULL) {
if (pool->base.stream_enc[i]->vpg != NULL) {
@@ -2042,6 +2051,16 @@ static bool dcn60_resource_construct(
dc->caps.utm_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
+ pool->base.ctx = ctx;
+
+ if (dc->config.lsdma_peak_bw_contention_support) {
+ pool->base.lsdma_scratch.buffer = dm_helpers_allocate_gpu_mem(ctx,
+ DC_MEM_ALLOC_TYPE_GART, LSDMA_CONTENTION_BUFFER_SIZE,
+ &pool->base.lsdma_scratch.pa);
+ if (pool->base.lsdma_scratch.buffer)
+ pool->base.lsdma_scratch.size = LSDMA_CONTENTION_BUFFER_SIZE;
+ }
+
if (ASICREV_IS_GC_12_0_1_A0(dc->ctx->asic_id.hw_internal_rev))
dc->caps.dcc_plane_width_limit = 7680;