summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWenjing Liu <wenjing.liu@amd.com>2026-06-24 23:17:40 -0400
committerAlex Deucher <alexander.deucher@amd.com>2026-07-28 18:37:33 -0400
commit52771a3254eabb37de2d53aa7b623a5487776ee8 (patch)
tree91f72717b47b2d02046a3c72ede509422b12bcce
parent85df2884384beb20753e96e795f38621e9c465fa (diff)
downloadlinux-next-52771a3254eabb37de2d53aa7b623a5487776ee8.tar.gz
linux-next-52771a3254eabb37de2d53aa7b623a5487776ee8.zip
drm/amd/display: Wire probe commit path into dc_update_state
[Why] The dc_update_state commit interface and the dc_probe object model existed but were not connected: there was no path to validate, commit, and program a probe through the update pipeline, nor to read the result back. The measurement waits on OTG frame edges and must not block fast updates. [How] - Move the per-commit scratch off dc_stream_state onto a dc-owned pool; acquire at init, release on cleanup or prepare failure, serialized by the DM global lock every commit path already holds. - dc_update_state drives stream commits, probe-only commits, or both through one staged init/prepare/execute/cleanup loop with a null-arg guard. prepare releases the scratch slot on failure; cleanup releases it on success. - Probe prepare installs the absolute probe set in place; execute calls the perfmon programming hook in the unlocked window and latches results. - Add resource_validate_probe_set, a context-free achievability check shared by dc_validate_global_state (full update) and probe prepare (probe-only). - dc_state_get_status gains a probe status class with a probe filter and a by-type mapping; dc_validation_set gains probes/probe_count. Drop the dead actual_* fields from struct dc_qos_info. Reviewed-by: Dominik Kaszewski <dominik.kaszewski@amd.com> Signed-off-by: Wenjing Liu <wenjing.liu@amd.com> Signed-off-by: Wayne Lin <wayne.lin@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.c314
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_state.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stream.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h122
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h27
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_types.h56
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/resource.h4
11 files changed, 428 insertions, 180 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index c4f5fdeb411c..78408fc60eb8 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -898,6 +898,32 @@ void dc_stream_set_static_screen_params(struct dc *dc,
dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, params);
}
+static void dc_destruct_update_scratch_pool(struct dc *dc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) {
+ kfree(dc->update_scratch_pool[i]);
+ dc->update_scratch_pool[i] = NULL;
+ dc->update_scratch_in_use[i] = false;
+ }
+}
+
+static bool dc_construct_update_scratch_pool(struct dc *dc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) {
+ dc->update_scratch_pool[i] = kzalloc(
+ sizeof(struct dc_update_scratch_space), GFP_KERNEL);
+ if (!dc->update_scratch_pool[i])
+ return false;
+ dc->update_scratch_in_use[i] = false;
+ }
+
+ return true;
+}
+
static void dc_destruct(struct dc *dc)
{
// reset link encoder assignment table on destruct
@@ -905,6 +931,8 @@ static void dc_destruct(struct dc *dc)
!dc->config.unify_link_enc_assignment)
link_enc_cfg_init(dc, dc->current_state);
+ dc_destruct_update_scratch_pool(dc);
+
if (dc->current_state) {
dc_state_release(dc->current_state);
dc->current_state = NULL;
@@ -1148,6 +1176,11 @@ static bool dc_construct(struct dc *dc,
goto fail;
}
+ if (!dc_construct_update_scratch_pool(dc)) {
+ dm_error("%s: failed to create update scratch pool\n", __func__);
+ goto fail;
+ }
+
return true;
fail:
@@ -3234,11 +3267,6 @@ static struct dc_update_descriptor check_update_surfaces_for_stream(
return overall_type;
}
-/*
- * dc_check_update_surfaces_for_stream() - Determine update type (fast, med, or full)
- *
- * See :c:type:`enum dc_update_type <dc_update_type>` for explanation of update types
- */
/**
* dc_check_state_update - Classify a dc_state_update by locking / re-entrancy requirements.
* @check_config: ASIC capabilities and display configuration context
@@ -3246,7 +3274,8 @@ static struct dc_update_descriptor check_update_surfaces_for_stream(
*
* Determines whether the update requires a fast, medium, or full lock
* by inspecting the stream, stream_update, and surface_updates carried on
- * the root object. Perfmon classification is reserved for a future slice.
+ * the root object. A probe update elevates the result to at least MED with
+ * the PROBE lock, so a probe-carrying commit takes the probe mutex.
*
* Return: dc_update_descriptor with update_type and lock_descriptor.
*/
@@ -3254,13 +3283,20 @@ struct dc_update_descriptor dc_check_state_update(
const struct dc_check_config *check_config,
struct dc_state_update *updates)
{
+ struct dc_update_descriptor desc = {0};
+
if (updates->stream_update)
stream_update_flags_clear(&updates->stream_update->stream->update_flags);
for (int i = 0; i < updates->surface_count; i++)
dc_pipe_update_bits_clear(&updates->surface_updates[i].surface->update_bits);
- return check_update_surfaces_for_stream(check_config, updates->surface_updates,
+ desc = check_update_surfaces_for_stream(check_config, updates->surface_updates,
updates->surface_count, updates->stream_update);
+
+ if (updates->probe_updates && updates->probe_updates->probe_count > 0)
+ elevate_update_type(&desc, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_PROBE);
+
+ return desc;
}
/**
@@ -3282,9 +3318,11 @@ struct dc_update_descriptor dc_check_update_surfaces_for_stream(
struct dc_stream_update *stream_update)
{
struct dc_state_update root = {
+ .stream = stream_update ? stream_update->stream : NULL,
+ .stream_update = stream_update,
.surface_updates = updates,
.surface_count = surface_count,
- .stream_update = stream_update,
+ .probe_updates = NULL
};
return dc_check_state_update(check_config, &root);
@@ -3703,13 +3741,6 @@ static bool full_update_required_weak(
const struct dc_stream_update *stream_update,
const struct dc_stream_state *stream);
-struct pipe_split_policy_backup {
- bool dynamic_odm_policy;
- bool subvp_policy;
- enum pipe_split_policy mpc_policy;
- char force_odm[MAX_PIPES];
-};
-
static void backup_and_set_minimal_pipe_split_policy(struct dc *dc,
struct dc_state *context,
struct pipe_split_policy_backup *policy)
@@ -6196,30 +6227,60 @@ static void clear_update_bits(struct dc_surface_update *srf_updates,
dc_pipe_update_bits_clear(&srf_updates[i].surface->update_bits);
}
+static struct dc_update_scratch_space *dc_update_scratch_acquire(struct dc *dc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) {
+ if (dc->update_scratch_in_use[i])
+ continue;
+
+ dc->update_scratch_in_use[i] = true;
+ return dc->update_scratch_pool[i];
+ }
+
+ /* TODO: add recoverable scratch acquisition failure handling. */
+ ASSERT(false);
+ return NULL;
+}
+
+static void dc_update_scratch_release(struct dc *dc,
+ struct dc_update_scratch_space *scratch)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) {
+ if (dc->update_scratch_pool[i] == scratch) {
+ dc->update_scratch_in_use[i] = false;
+ return;
+ }
+ }
+}
+
/**
* dc_update_state - Commit an absolute dc_state_update.
* @dc: DC structure
* @updates: root update object carrying stream, plane, and probe updates
- *
- * When stream is non-NULL the stream and its plane updates are committed via
- * the init/prepare/execute/cleanup pipeline. Probe commit is reserved for a
- * future slice. dc_update_planes_and_stream() is now a shim over this function.
- *
* Return: true on success, false on failure.
*/
bool dc_update_state(struct dc *dc, struct dc_state_update *updates)
{
- if (updates->stream != NULL) {
- struct dc_update_scratch_space *scratch = dc_update_state_init(dc, updates);
- bool more = true;
+ struct dc_update_scratch_space *scratch;
+ bool more = true;
- while (more) {
- if (!dc_update_state_prepare(scratch))
- return false;
+ if (!dc || !updates)
+ return false;
- dc_update_state_execute(scratch);
- more = dc_update_state_cleanup(scratch);
- }
+ scratch = dc_update_state_init(dc, updates);
+ if (!scratch)
+ return false;
+
+ while (more) {
+ if (!dc_update_state_prepare(scratch))
+ return false;
+
+ dc_update_state_execute(scratch);
+ more = dc_update_state_cleanup(scratch);
}
return true;
@@ -8128,8 +8189,6 @@ bool dc_get_qos_info(struct dc *dc, struct dc_qos_info *info)
memset(info, 0, sizeof(*info));
- /* TODO: remove the actual_* fields from struct dc_qos_info once all callers
- * read measured QoS from dc_state probe_status instead of this struct. */
info->dcn_bandwidth_ub_in_mbps = (uint32_t)(clk->fclk_khz / 1000 * 64);
if (dc->clk_mgr && dc->clk_mgr->funcs->get_requested_memory_qos) {
@@ -8155,38 +8214,6 @@ unsigned int dc_override_memory_bandwidth_request(
dc->clk_mgr, bw_mbps * 1000) / 1000;
}
-enum update_v3_flow {
- UPDATE_V3_FLOW_INVALID,
- UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST,
- UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL,
- UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS,
- UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW,
- UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT,
-};
-
-struct dc_update_scratch_space {
- struct dc *dc;
- struct dc_surface_update *surface_updates;
- int surface_count;
- struct dc_stream_state *stream;
- struct dc_stream_update *stream_update;
- bool update_v3;
- bool do_clear_update_bits;
- enum dc_update_type update_type;
- struct dc_state *new_context;
- enum update_v3_flow flow;
- struct dc_state *backup_context;
- struct dc_state *intermediate_context;
- struct pipe_split_policy_backup intermediate_policy;
- struct dc_surface_update intermediate_updates[MAX_SURFACES];
- int intermediate_count;
-};
-
-size_t dc_update_scratch_space_size(void)
-{
- return sizeof(struct dc_update_scratch_space);
-}
-
static bool update_planes_and_stream_prepare_v2(
struct dc_update_scratch_space *scratch
)
@@ -8353,6 +8380,19 @@ static bool update_planes_and_stream_prepare_v3(
return false;
}
+/**
+ * should_commit_intermediate_context - Does this flow commit a transient
+ * minimal-transition intermediate context
+ * @flow: the commit flow selected for this iteration
+ *
+ * Return: true if this iteration commits the intermediate context.
+ */
+static bool should_commit_intermediate_context(enum update_v3_flow flow)
+{
+ return flow == UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW
+ || flow == UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT;
+}
+
static void update_planes_and_stream_execute_v3_commit(
const struct dc_update_scratch_space *scratch,
bool intermediate_update,
@@ -8376,6 +8416,8 @@ static void update_planes_and_stream_execute_v3(
const struct dc_update_scratch_space *scratch
)
{
+ bool intermediate_context = should_commit_intermediate_context(scratch->flow);
+
switch (scratch->flow) {
case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST:
commit_planes_for_stream_fast(
@@ -8391,16 +8433,16 @@ static void update_planes_and_stream_execute_v3(
case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL:
case UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS:
- update_planes_and_stream_execute_v3_commit(scratch, false, false, true);
+ update_planes_and_stream_execute_v3_commit(scratch, false, intermediate_context, true);
break;
case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW:
- update_planes_and_stream_execute_v3_commit(scratch, false, true,
+ update_planes_and_stream_execute_v3_commit(scratch, false, intermediate_context,
scratch->dc->check_config.deferred_transition_state);
break;
case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT:
- update_planes_and_stream_execute_v3_commit(scratch, true, true, false);
+ update_planes_and_stream_execute_v3_commit(scratch, true, intermediate_context, false);
break;
case UPDATE_V3_FLOW_INVALID:
@@ -8481,45 +8523,139 @@ struct dc_update_scratch_space *dc_update_state_init(
)
{
const enum dce_version version = dc->ctx->dce_version;
- struct dc_update_scratch_space *scratch = updates->stream->update_scratch;
-
- *scratch = (struct dc_update_scratch_space){
- .dc = dc,
- .surface_updates = updates->surface_updates,
- .surface_count = updates->surface_count,
- .stream = updates->stream,
- .stream_update = updates->stream_update,
- .update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21,
- .do_clear_update_bits = version >= DCN_VERSION_1_0,
- };
+ struct dc_update_scratch_space *scratch = dc_update_scratch_acquire(dc);
+ bool has_stream_or_plane = updates->stream || updates->stream_update || updates->surface_updates;
+ bool has_probe = updates->probe_updates;
+ bool surface_without_stream = updates->surface_updates && !updates->stream;
+ bool stream_update_without_stream = updates->stream_update && !updates->stream;
+ bool bad_surface_count = updates->surface_count > 0 && !updates->surface_updates;
+
+ if (!scratch)
+ return NULL;
+
+ if (!has_stream_or_plane && !has_probe) {
+ dc_update_scratch_release(dc, scratch);
+ return NULL;
+ }
+
+ if (surface_without_stream || stream_update_without_stream || bad_surface_count) {
+ dc_update_scratch_release(dc, scratch);
+ return NULL;
+ }
+
+ memset(scratch, 0, sizeof(*scratch));
+
+ scratch->dc = dc;
+ scratch->surface_updates = updates->surface_updates;
+ scratch->surface_count = updates->surface_count;
+ scratch->stream = updates->stream;
+ scratch->stream_update = updates->stream_update;
+ scratch->probe_updates = updates->probe_updates;
+ scratch->update_v3 = version >= DCN_VERSION_4_01
+ || version == DCN_VERSION_3_2
+ || version == DCN_VERSION_3_21;
+ scratch->do_clear_update_bits = version >= DCN_VERSION_1_0;
+ scratch->new_context = NULL;
+ scratch->flow = UPDATE_V3_FLOW_INVALID;
return scratch;
}
-bool dc_update_state_prepare(
- struct dc_update_scratch_space *scratch
-)
+/**
+ * dc_update_probes_prepare - Commit the desired probe set into new_context.
+ * @scratch: commit scratch carrying the probe updates
+ *
+ * Return: true on success or when there is nothing to do; false when the
+ * desired set is unachievable.
+ */
+static bool dc_update_probes_prepare(struct dc_update_scratch_space *scratch)
+{
+ struct dc *dc = scratch->dc;
+ const struct dc_probe_updates *probe_updates = scratch->probe_updates;
+ uint8_t i;
+
+ if (!probe_updates)
+ return true;
+
+ if (resource_validate_probe_set(dc, probe_updates->probes,
+ (uint8_t)probe_updates->probe_count) != DC_OK)
+ return false;
+
+ if (!scratch->new_context)
+ scratch->new_context = dc->current_state;
+
+ for (i = 0; i < probe_updates->probe_count && i < MAX_PROBES; i++)
+ scratch->new_context->probes[i] = probe_updates->probes[i];
+ scratch->new_context->probe_count = probe_updates->probe_count;
+
+ return true;
+}
+
+/**
+ * dc_update_probes_execute - Program the committed probes.
+ * @scratch: commit scratch carrying the probe updates
+ *
+ */
+static void dc_update_probes_execute(const struct dc_update_scratch_space *scratch)
+{
+ struct dc *dc = scratch->dc;
+
+ if (should_commit_intermediate_context(scratch->flow))
+ return;
+
+ if (dc->hwss.program_perfmon)
+ dc->hwss.program_perfmon(dc, scratch->new_context);
+}
+
+bool dc_update_state_prepare(struct dc_update_scratch_space *scratch)
{
- return scratch->update_v3
- ? update_planes_and_stream_prepare_v3(scratch)
- : update_planes_and_stream_prepare_v2(scratch);
+ if (scratch->stream) {
+ bool ok = scratch->update_v3
+ ? update_planes_and_stream_prepare_v3(scratch)
+ : update_planes_and_stream_prepare_v2(scratch);
+
+ if (!ok)
+ goto release_scratch;
+ }
+
+ if (!dc_update_probes_prepare(scratch))
+ goto release_scratch;
+
+ return true;
+
+release_scratch:
+ /* execute and cleanup never run on this path, so release here. */
+ dc_update_scratch_release(scratch->dc, scratch);
+ return false;
}
void dc_update_state_execute(
const struct dc_update_scratch_space *scratch
)
{
- scratch->update_v3
- ? update_planes_and_stream_execute_v3(scratch)
- : update_planes_and_stream_execute_v2(scratch);
+ if (scratch->stream)
+ scratch->update_v3
+ ? update_planes_and_stream_execute_v3(scratch)
+ : update_planes_and_stream_execute_v2(scratch);
+
+ if (scratch->probe_updates)
+ dc_update_probes_execute(scratch);
}
bool dc_update_state_cleanup(
struct dc_update_scratch_space *scratch
)
{
- return scratch->update_v3
- ? update_planes_and_stream_cleanup_v3(scratch)
- : update_planes_and_stream_cleanup_v2(scratch);
+ bool more = false;
+
+ if (scratch->stream)
+ more = scratch->update_v3
+ ? update_planes_and_stream_cleanup_v3(scratch)
+ : update_planes_and_stream_cleanup_v2(scratch);
+
+ if (!more)
+ dc_update_scratch_release(scratch->dc, scratch);
+
+ return more;
}
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 07ec00e11f2d..8b54bab98283 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
@@ -3388,7 +3388,7 @@ void hwss_hubbub_perfmon_get_memory_latencies(union block_sequence_params *param
{
struct hubbub *hubbub = params->hubbub_perfmon_get_memory_latencies_params.hubbub;
uint32_t refclk_mhz = params->hubbub_perfmon_get_memory_latencies_params.refclk_mhz;
- struct hubbub_system_latencies *result = params->hubbub_perfmon_get_memory_latencies_params.result;
+ struct dc_probe_latencies *result = params->hubbub_perfmon_get_memory_latencies_params.result;
if (hubbub && hubbub->funcs->perfmon.get_memory_latencies_ns && result)
hubbub->funcs->perfmon.get_memory_latencies_ns(
@@ -4711,7 +4711,7 @@ void hwss_add_hubbub_perfmon_get_in_order_bw(struct block_sequence_state *seq_st
void hwss_add_hubbub_perfmon_get_memory_latencies(struct block_sequence_state *seq_state,
struct hubbub *hubbub, uint32_t refclk_mhz,
- struct hubbub_system_latencies *result)
+ struct dc_probe_latencies *result)
{
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_MEMORY_LATENCIES;
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 d9492a460c2a..b970f152d67f 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4384,6 +4384,40 @@ static bool add_all_planes_for_stream(
}
/**
+ * resource_validate_probe_set - Validate a probe descriptor set against the ASIC.
+ * @dc: DC instance providing the HWSS capability hooks
+ * @probes: desired probe descriptors
+ * @probe_count: number of valid entries in @probes
+ *
+ * Return: DC_OK if achievable, otherwise a DC error.
+ */
+enum dc_status resource_validate_probe_set(struct dc *dc,
+ const struct dc_probe_state *probes,
+ uint8_t probe_count)
+{
+ uint8_t i;
+
+ if (probe_count == 0)
+ return DC_OK;
+
+ if (!dc->hwss.program_perfmon)
+ return DC_NOT_SUPPORTED;
+
+ if (probe_count > MAX_PROBES)
+ 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)
+ return DC_NOT_SUPPORTED;
+ }
+
+ return DC_OK;
+}
+
+/**
* dc_validate_with_context - Validate and update the potential new stream in the context object
*
* @dc: Used to get the current state status
@@ -4754,6 +4788,10 @@ enum dc_status dc_validate_global_state(
if (result == DC_OK)
result = dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, validate_mode);
+ if (result == DC_OK)
+ result = resource_validate_probe_set(dc, new_ctx->probes,
+ (uint8_t)new_ctx->probe_count);
+
return result;
}
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 3e819997aafa..b534b1a641af 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
@@ -708,6 +708,17 @@ enum dc_status dc_state_get_status(struct dc_state_status *status,
}
}
+ if (options->types & DC_GET_STATUS_PROBE) {
+ status->probe_count = 0;
+ for (i = 0; i < options->state->probe_count; i++) {
+ if (options->probe &&
+ options->probe->type != options->state->probes[i].type)
+ continue;
+ status->probe_status[status->probe_count++] =
+ &options->state->probe_status[i];
+ }
+ }
+
return DC_OK;
}
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 5a46d91e2f37..25f904eccb10 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -194,7 +194,6 @@ static void dc_stream_free(struct kref *kref)
struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount);
dc_stream_destruct(stream);
- kfree(stream->update_scratch);
kfree(stream);
}
@@ -219,13 +218,6 @@ struct dc_stream_state *dc_create_stream_for_sink(
if (stream == NULL)
goto fail;
- DC_RUN_WITH_PREEMPTION_ENABLED(stream->update_scratch =
- kzalloc((int32_t) dc_update_scratch_space_size(),
- GFP_ATOMIC));
-
- if (stream->update_scratch == NULL)
- goto fail;
-
if (dc_stream_construct(stream, sink) == false)
goto fail;
@@ -234,10 +226,8 @@ struct dc_stream_state *dc_create_stream_for_sink(
return stream;
fail:
- if (stream) {
- kfree(stream->update_scratch);
+ if (stream)
kfree(stream);
- }
return NULL;
}
@@ -250,16 +240,6 @@ struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream)
if (!new_stream)
return NULL;
- // Scratch is not meant to be reused across copies, as might have self-referential pointers
- new_stream->update_scratch = kzalloc(
- (int32_t) dc_update_scratch_space_size(),
- GFP_KERNEL
- );
- if (!new_stream->update_scratch) {
- kfree(new_stream);
- return NULL;
- }
-
if (new_stream->sink)
dc_sink_retain(new_stream->sink);
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 2da89c7470de..0913fab7504c 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -530,6 +530,7 @@ enum dc_lock_descriptor {
LOCK_DESCRIPTOR_STREAM = 0x1,
LOCK_DESCRIPTOR_LINK = 0x2,
LOCK_DESCRIPTOR_GLOBAL = 0x4,
+ LOCK_DESCRIPTOR_PROBE = 0x8,
};
struct dc_update_descriptor {
@@ -1018,14 +1019,9 @@ struct dc_bounding_box_overrides {
};
struct dc_qos_info {
- uint32_t actual_peak_bw_in_mbps;
uint32_t qos_bandwidth_lb_in_mbps;
- uint32_t actual_avg_bw_in_mbps;
uint32_t calculated_avg_bw_in_mbps;
- uint32_t actual_max_latency_in_ns;
- uint32_t actual_min_latency_in_ns;
uint32_t qos_max_latency_ub_in_ns;
- uint32_t actual_avg_latency_in_ns;
uint32_t qos_avg_latency_ub_in_ns;
uint32_t dcn_bandwidth_ub_in_mbps;
uint32_t qos_max_bw_budget_in_mbps;
@@ -1987,6 +1983,12 @@ struct dc {
struct dc_state *current_state;
struct resource_pool *res_pool;
+ /**
+ * @update_scratch_pool: Per-commit scratch buffers for dc_update_state.
+ */
+ struct dc_update_scratch_space *update_scratch_pool[MAX_STREAMS + 1];
+ bool update_scratch_in_use[MAX_STREAMS + 1];
+
struct clk_mgr *clk_mgr;
/* Display Engine Clock levels */
@@ -2115,24 +2117,106 @@ struct dc_state_update {
};
/**
+ * dc_check_state_update() - Classify an update without committing it.
+ * @check_config: DC check configuration
+ * @updates: root update object to classify
+ *
+ * Return: descriptor indicating update type and required lock scope.
+ */
+struct dc_update_descriptor dc_check_state_update(
+ const struct dc_check_config *check_config,
+ struct dc_state_update *updates);
+
+/**
* dc_update_state - Commit an absolute dc_state_update.
* @dc: DC structure
* @updates: root update object carrying stream, plane, and probe updates
*
- * When stream is non-NULL the stream and its plane updates are committed via
- * the init/prepare/execute/cleanup pipeline. Probe commit is reserved for a
- * future slice. dc_update_planes_and_stream() is now a shim over this function.
- *
* Return: true on success, false on failure.
*/
bool dc_update_state(struct dc *dc, struct dc_state_update *updates);
+struct dc_update_scratch_space;
+
+/**
+ * dc_update_state_init - Acquire and initialise a commit scratch buffer.
+ * @dc: DC structure
+ * @updates: update descriptor; validated before the slot is acquired
+ *
+ * Return: a scratch slot on success, NULL if validation fails or the pool
+ * is exhausted. The slot must be released via dc_update_state_cleanup() on
+ * success, or automatically by dc_update_state_prepare() on failure.
+ */
+struct dc_update_scratch_space *dc_update_state_init(
+ struct dc *dc,
+ const struct dc_state_update *updates
+);
+
+/**
+ * dc_update_state_prepare - Prepare the commit under the global lock.
+ * @scratch: commit scratch from dc_update_state_init()
+ *
+ * On failure the scratch slot is released and false is returned; the caller
+ * must not call execute or cleanup.
+ */
+bool dc_update_state_prepare(struct dc_update_scratch_space *scratch);
+
+/**
+ * dc_update_state_execute - Program hardware; called without the global lock.
+ * @scratch: commit scratch from dc_update_state_init()
+ */
+void dc_update_state_execute(const struct dc_update_scratch_space *scratch);
+
+/**
+ * dc_update_state_cleanup - Finalise the commit and release the scratch slot.
+ * @scratch: commit scratch from dc_update_state_init()
+ *
+ * Must be called with the global lock held. Returns true if the caller must
+ * loop back to prepare (SEAMLESS continuation).
+ */
+bool dc_update_state_cleanup(struct dc_update_scratch_space *scratch);
+
+/**
+ * struct dc_probe_latencies - min/max/avg memory latency in ns.
+ * @max_latency_ns: maximum latency in nanoseconds
+ * @avg_latency_ns: average latency in nanoseconds
+ * @min_latency_ns: minimum latency in nanoseconds
+ */
+struct dc_probe_latencies {
+ uint32_t max_latency_ns;
+ uint32_t avg_latency_ns;
+ uint32_t min_latency_ns;
+};
+
+/**
+ * struct dc_probe_status - results for a probe.
+ * @valid: true if a measurement was latched.
+ * @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),
+ * stored as struct dc_probe_latencies.
+ * @u.urgent_assertion_count: number of urgent assertion events (DC_PROBE_URGENT_ASSERTION_COUNT).
+ * @u.prefetch_data_size: total prefetch data in bytes (DC_PROBE_PREFETCH_DATA_SIZE).
+ */
+struct dc_probe_status {
+ bool valid;
+ enum dc_probe_type type;
+ union {
+ uint32_t bandwidth_mbps;
+ struct dc_probe_latencies latency;
+ uint32_t urgent_assertion_count;
+ uint32_t prefetch_data_size;
+ } u;
+};
+
/**
* enum dc_get_status_type - Bitmask selecting which status classes to populate.
* @DC_GET_STATUS_STREAM: populate stream_status fields in dc_state_status
+ * @DC_GET_STATUS_PROBE: populate probe_status fields in dc_state_status
*/
enum dc_get_status_type {
DC_GET_STATUS_STREAM = (1u << 0),
+ DC_GET_STATUS_PROBE = (1u << 1),
};
/**
@@ -2141,21 +2225,28 @@ enum dc_get_status_type {
* @types: OR of dc_get_status_type values selecting classes to populate
* @stream: optional stream filter for DC_GET_STATUS_STREAM. NULL means
* populate status for all streams in the state
+ * @probe: optional probe filter for DC_GET_STATUS_PROBE. NULL means
+ * populate status for all probes in the state
*/
struct dc_get_status_options {
struct dc_state *state;
uint32_t types;
const struct dc_stream_state *stream;
+ const struct dc_probe_state *probe;
};
/**
* struct dc_state_status - Output-only status object from dc_state_get_status.
* @stream_count: number of valid entries in stream_status (DC_GET_STATUS_STREAM)
* @stream_status: pointers to live per-stream status entries
+ * @probe_count: number of valid entries in probe_status (DC_GET_STATUS_PROBE)
+ * @probe_status: pointers to live per-probe status entries
*/
struct dc_state_status {
int stream_count;
struct dc_stream_status *stream_status[MAX_STREAMS];
+ int probe_count;
+ struct dc_probe_status *probe_status[MAX_PROBES];
};
/**
@@ -2163,9 +2254,6 @@ struct dc_state_status {
* @status: output object populated according to options->types
* @options: selects the source state, status classes to fill, and filters
*
- * dc_state_get_stream_status() is a thin shim over this function with
- * types = DC_GET_STATUS_STREAM and a stream filter.
- *
* Return: DC_OK on success, DC_ERROR_UNEXPECTED if state is NULL.
*/
enum dc_status dc_state_get_status(struct dc_state_status *status,
@@ -2258,6 +2346,16 @@ struct dc_validation_set {
* @stream_count: Number of active entries in @streams
*/
uint8_t stream_count;
+
+ /**
+ * @probes: Global probe descriptors to validate alongside the streams
+ */
+ struct dc_probe_state probes[MAX_PROBES];
+
+ /**
+ * @probe_count: Number of active entries in @probes
+ */
+ uint8_t probe_count;
};
bool dc_validate_boot_timing(const struct dc *dc,
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index a866688ad9db..4530f294f1c2 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -329,7 +329,6 @@ struct dc_stream_state {
enum dc_drr_trigger_mode drr_trigger_mode;
- struct dc_update_scratch_space *update_scratch;
bool firmware_controlled_hdr_info_packet;
};
@@ -408,30 +407,8 @@ bool dc_update_planes_and_stream(struct dc *dc,
struct dc_stream_state *dc_stream,
struct dc_stream_update *stream_update);
-struct dc_update_scratch_space;
struct dc_state_update;
-size_t dc_update_scratch_space_size(void);
-
-struct dc_update_scratch_space *dc_update_state_init(
- struct dc *dc,
- const struct dc_state_update *updates
-);
-
-// Locked, false is failed
-bool dc_update_state_prepare(
- struct dc_update_scratch_space *scratch
-);
-
-// Unlocked
-void dc_update_state_execute(
- const struct dc_update_scratch_space *scratch
-);
-
-// Locked, true if call again
-bool dc_update_state_cleanup(
- struct dc_update_scratch_space *scratch
-);
/*
* Set up surface attributes and associate to a stream
@@ -516,10 +493,6 @@ void dc_enable_stereo(
/* Triggers multi-stream synchronization. */
void dc_trigger_sync(struct dc *dc, struct dc_state *context);
-struct dc_update_descriptor dc_check_state_update(
- const struct dc_check_config *check_config,
- struct dc_state_update *updates);
-
/* Shim: packs args into dc_state_update and calls dc_check_state_update(). */
struct dc_update_descriptor dc_check_update_surfaces_for_stream(
const struct dc_check_config *check_config,
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 6754da1e6ee2..4549c435501a 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -631,7 +631,7 @@ struct hubbub_perfmon_get_in_order_bw_params {
struct hubbub_perfmon_get_memory_latencies_params {
struct hubbub *hubbub;
uint32_t refclk_mhz;
- struct hubbub_system_latencies *result;
+ struct dc_probe_latencies *result;
};
struct hubbub_perfmon_get_urgent_assertion_count_params {
@@ -2377,7 +2377,7 @@ void hwss_add_hubbub_perfmon_get_in_order_bw(struct block_sequence_state *seq_st
void hwss_add_hubbub_perfmon_get_memory_latencies(struct block_sequence_state *seq_state,
struct hubbub *hubbub, uint32_t refclk_mhz,
- struct hubbub_system_latencies *result);
+ struct dc_probe_latencies *result);
void hwss_add_hubbub_perfmon_get_urgent_assertion_count(struct block_sequence_state *seq_state,
struct hubbub *hubbub, uint32_t refclk_mhz,
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 ac3e9eaa569c..e87f9fd60bcb 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
@@ -611,27 +611,6 @@ struct dc_dmub_cmd {
};
/**
- * struct dc_probe_status - DC-internal latched perfmon results for a probe.
- * @valid: true if a measurement was latched this commit.
- * @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),
- * stored as struct hubbub_system_latencies.
- * @u.urgent_assertion_count: number of urgent assertion events (DC_PROBE_URGENT_ASSERTION_COUNT).
- * @u.prefetch_data_size: total prefetch data in bytes (DC_PROBE_PREFETCH_DATA_SIZE).
- */
-struct dc_probe_status {
- bool valid;
- enum dc_probe_type type;
- union {
- uint32_t bandwidth_mbps;
- struct hubbub_system_latencies latency;
- uint32_t urgent_assertion_count;
- uint32_t prefetch_data_size;
- } u;
-};
-
-/**
* struct dc_state - The full description of a state requested by users
*/
struct dc_state {
@@ -774,4 +753,39 @@ struct dc_requested_memory_qos {
uint32_t max_bw_budget_in_mbps;
};
+enum update_v3_flow {
+ UPDATE_V3_FLOW_INVALID,
+ UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST,
+ UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL,
+ UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS,
+ UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW,
+ UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT,
+};
+
+struct pipe_split_policy_backup {
+ bool dynamic_odm_policy;
+ bool subvp_policy;
+ enum pipe_split_policy mpc_policy;
+ char force_odm[MAX_PIPES];
+};
+
+struct dc_update_scratch_space {
+ struct dc *dc;
+ struct dc_surface_update *surface_updates;
+ int surface_count;
+ struct dc_stream_state *stream;
+ struct dc_stream_update *stream_update;
+ const struct dc_probe_updates *probe_updates;
+ bool update_v3;
+ bool do_clear_update_bits;
+ enum dc_update_type update_type;
+ struct dc_state *new_context;
+ enum update_v3_flow flow;
+ struct dc_state *backup_context;
+ struct dc_state *intermediate_context;
+ struct pipe_split_policy_backup intermediate_policy;
+ struct dc_surface_update intermediate_updates[MAX_SURFACES];
+ int intermediate_count;
+};
+
#endif /* _CORE_TYPES_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
index fd742b320128..02bc0010e565 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
@@ -145,12 +145,6 @@ struct dcn_hubbub_reg_state {
uint32_t compbuf_ctrl;
};
-struct hubbub_system_latencies {
- uint32_t max_latency_ns;
- uint32_t avg_latency_ns;
- uint32_t min_latency_ns;
-};
-
struct hubbub_urgent_latency_params {
uint32_t refclk_mhz;
uint32_t t_win_ns;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h
index b64ba8c0adb1..d3171e70e07c 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/resource.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h
@@ -582,6 +582,10 @@ bool resource_validate_attach_surfaces(
struct dc_state *context,
const struct resource_pool *pool);
+enum dc_status resource_validate_probe_set(struct dc *dc,
+ const struct dc_probe_state *probes,
+ uint8_t probe_count);
+
enum dc_status resource_map_clock_resources(
const struct dc *dc,
struct dc_state *context,