Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/amd/display: Split update_planes_and_stream_v3 into parts (V2)

[Why]
Currently all of the preparation and execution of plane update is done
under a DC lock, blocking other code from accessing DC for longer than
strictly necessary.

[How]
Break the v3 update flow into 3 parts:
* prepare - locked, calculate update flow and modify DC state
* execute - unlocked, program hardware
* cleanup - locked, finalize DC state and free temp resources
Legacy v2 flow too compilicated to break down for now, link new API
with old by executing everything in slightly misnamed prepare stage.

V2:
Keep the new code structure, but point all users back at the old code,
until fully tested.

Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Dominik Kaszewski <dominik.kaszewski@amd.com>
Signed-off-by: Roman Li <roman.li@amd.com>
Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Dominik Kaszewski and committed by
Alex Deucher
d38ec099 cb7a978c

+403 -10
+349 -2
drivers/gpu/drm/amd/display/dc/core/dc.c
··· 3849 3849 void dc_dmub_update_dirty_rect(struct dc *dc, 3850 3850 int surface_count, 3851 3851 struct dc_stream_state *stream, 3852 - struct dc_surface_update *srf_updates, 3852 + const struct dc_surface_update *srf_updates, 3853 3853 struct dc_state *context) 3854 3854 { 3855 3855 union dmub_rb_cmd cmd; ··· 4154 4154 } 4155 4155 4156 4156 static void commit_planes_for_stream(struct dc *dc, 4157 - struct dc_surface_update *srf_updates, 4157 + const struct dc_surface_update *srf_updates, 4158 4158 int surface_count, 4159 4159 struct dc_stream_state *stream, 4160 4160 struct dc_stream_update *stream_update, ··· 7175 7175 7176 7176 return true; 7177 7177 } 7178 + 7179 + enum update_v3_flow { 7180 + UPDATE_V3_FLOW_INVALID, 7181 + UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST, 7182 + UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL, 7183 + UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS, 7184 + UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW, 7185 + UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT, 7186 + }; 7187 + 7188 + struct dc_update_scratch_space { 7189 + struct dc *dc; 7190 + struct dc_surface_update *surface_updates; 7191 + int surface_count; 7192 + struct dc_stream_state *stream; 7193 + struct dc_stream_update *stream_update; 7194 + bool update_v3; 7195 + bool do_clear_update_flags; 7196 + enum surface_update_type update_type; 7197 + struct dc_state *new_context; 7198 + enum update_v3_flow flow; 7199 + struct dc_state *backup_context; 7200 + struct dc_state *intermediate_context; 7201 + struct pipe_split_policy_backup intermediate_policy; 7202 + struct dc_surface_update intermediate_updates[MAX_SURFACES]; 7203 + int intermediate_count; 7204 + }; 7205 + 7206 + size_t dc_update_scratch_space_size(void) 7207 + { 7208 + return sizeof(struct dc_update_scratch_space); 7209 + } 7210 + 7211 + static bool update_planes_and_stream_prepare_v2( 7212 + struct dc_update_scratch_space *scratch 7213 + ) 7214 + { 7215 + // v2 is too tangled to break into stages, so just execute everything under lock 7216 + dc_exit_ips_for_hw_access(scratch->dc); 7217 + return update_planes_and_stream_v2( 7218 + scratch->dc, 7219 + scratch->surface_updates, 7220 + scratch->surface_count, 7221 + scratch->stream, 7222 + scratch->stream_update 7223 + ); 7224 + } 7225 + 7226 + static void update_planes_and_stream_execute_v2( 7227 + const struct dc_update_scratch_space *scratch 7228 + ) 7229 + { 7230 + // Nothing to do, see `update_planes_and_stream_prepare_v2` 7231 + (void) scratch; 7232 + } 7233 + 7234 + static bool update_planes_and_stream_cleanup_v2( 7235 + const struct dc_update_scratch_space *scratch 7236 + ) 7237 + { 7238 + if (scratch->do_clear_update_flags) 7239 + clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream); 7240 + 7241 + return false; 7242 + } 7243 + 7244 + static void update_planes_and_stream_cleanup_v3_intermediate( 7245 + struct dc_update_scratch_space *scratch, 7246 + bool backup 7247 + ); 7248 + 7249 + static bool update_planes_and_stream_prepare_v3_intermediate_seamless( 7250 + struct dc_update_scratch_space *scratch 7251 + ) 7252 + { 7253 + return is_pipe_topology_transition_seamless_with_intermediate_step( 7254 + scratch->dc, 7255 + scratch->dc->current_state, 7256 + scratch->intermediate_context, 7257 + scratch->new_context 7258 + ); 7259 + } 7260 + 7261 + static bool update_planes_and_stream_prepare_v3( 7262 + struct dc_update_scratch_space *scratch 7263 + ) 7264 + { 7265 + dc_exit_ips_for_hw_access(scratch->dc); 7266 + 7267 + if (!update_planes_and_stream_state( 7268 + scratch->dc, 7269 + scratch->surface_updates, 7270 + scratch->surface_count, 7271 + scratch->stream, 7272 + scratch->stream_update, 7273 + &scratch->update_type, 7274 + &scratch->new_context 7275 + )) { 7276 + return false; 7277 + } 7278 + 7279 + if (scratch->new_context == scratch->dc->current_state) { 7280 + ASSERT(scratch->update_type < UPDATE_TYPE_FULL); 7281 + 7282 + // TODO: Do we need this to be alive in execute? 7283 + struct dc_fast_update fast_update[MAX_SURFACES] = { 0 }; 7284 + 7285 + populate_fast_updates( 7286 + fast_update, 7287 + scratch->surface_updates, 7288 + scratch->surface_count, 7289 + scratch->stream_update 7290 + ); 7291 + const bool fast = fast_update_only( 7292 + scratch->dc, 7293 + fast_update, 7294 + scratch->surface_updates, 7295 + scratch->surface_count, 7296 + scratch->stream_update, 7297 + scratch->stream 7298 + ) 7299 + // TODO: Can this be used to skip `populate_fast_updates`? 7300 + && !scratch->dc->check_config.enable_legacy_fast_update; 7301 + scratch->flow = fast 7302 + ? UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST 7303 + : UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL; 7304 + return true; 7305 + } 7306 + 7307 + ASSERT(scratch->update_type >= UPDATE_TYPE_FULL); 7308 + 7309 + const bool seamless = scratch->dc->hwss.is_pipe_topology_transition_seamless( 7310 + scratch->dc, 7311 + scratch->dc->current_state, 7312 + scratch->new_context 7313 + ); 7314 + if (seamless) { 7315 + scratch->flow = UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS; 7316 + return true; 7317 + } 7318 + 7319 + scratch->intermediate_context = create_minimal_transition_state( 7320 + scratch->dc, 7321 + scratch->new_context, 7322 + &scratch->intermediate_policy 7323 + ); 7324 + if (scratch->intermediate_context) { 7325 + if (update_planes_and_stream_prepare_v3_intermediate_seamless(scratch)) { 7326 + scratch->flow = UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW; 7327 + return true; 7328 + } 7329 + 7330 + update_planes_and_stream_cleanup_v3_intermediate(scratch, false); 7331 + } 7332 + 7333 + restore_planes_and_stream_state(&scratch->dc->scratch.current_state, scratch->stream); 7334 + scratch->backup_context = scratch->dc->current_state; 7335 + dc_state_retain(scratch->backup_context); 7336 + scratch->intermediate_context = create_minimal_transition_state( 7337 + scratch->dc, 7338 + scratch->backup_context, 7339 + &scratch->intermediate_policy 7340 + ); 7341 + if (scratch->intermediate_context) { 7342 + if (update_planes_and_stream_prepare_v3_intermediate_seamless(scratch)) { 7343 + scratch->flow = UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT; 7344 + scratch->intermediate_count = initialize_empty_surface_updates( 7345 + scratch->stream, scratch->intermediate_updates 7346 + ); 7347 + return true; 7348 + } 7349 + 7350 + update_planes_and_stream_cleanup_v3_intermediate(scratch, true); 7351 + } 7352 + 7353 + scratch->flow = UPDATE_V3_FLOW_INVALID; 7354 + dc_state_release(scratch->backup_context); 7355 + restore_planes_and_stream_state(&scratch->dc->scratch.new_state, scratch->stream); 7356 + return false; 7357 + } 7358 + 7359 + static void update_planes_and_stream_execute_v3_commit( 7360 + const struct dc_update_scratch_space *scratch, 7361 + bool intermediate_update, 7362 + bool intermediate_context 7363 + ) 7364 + { 7365 + commit_planes_for_stream( 7366 + scratch->dc, 7367 + intermediate_update ? scratch->intermediate_updates : scratch->surface_updates, 7368 + intermediate_update ? scratch->intermediate_count : scratch->surface_count, 7369 + scratch->stream, 7370 + intermediate_context ? NULL : scratch->stream_update, 7371 + intermediate_context ? UPDATE_TYPE_FULL : scratch->update_type, 7372 + // `dc->current_state` only used in `NO_NEW_CONTEXT`, where it is equal to `new_context` 7373 + intermediate_context ? scratch->intermediate_context : scratch->new_context 7374 + ); 7375 + } 7376 + 7377 + static void update_planes_and_stream_execute_v3( 7378 + const struct dc_update_scratch_space *scratch 7379 + ) 7380 + { 7381 + switch (scratch->flow) { 7382 + case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST: 7383 + commit_planes_for_stream_fast( 7384 + scratch->dc, 7385 + scratch->surface_updates, 7386 + scratch->surface_count, 7387 + scratch->stream, 7388 + scratch->stream_update, 7389 + scratch->update_type, 7390 + scratch->new_context 7391 + ); 7392 + break; 7393 + 7394 + case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL: 7395 + case UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS: 7396 + update_planes_and_stream_execute_v3_commit(scratch, false, false); 7397 + break; 7398 + 7399 + case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW: 7400 + update_planes_and_stream_execute_v3_commit(scratch, false, true); 7401 + update_planes_and_stream_execute_v3_commit(scratch, false, false); 7402 + break; 7403 + 7404 + case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT: 7405 + update_planes_and_stream_execute_v3_commit(scratch, true, true); 7406 + update_planes_and_stream_execute_v3_commit(scratch, false, false); 7407 + break; 7408 + 7409 + case UPDATE_V3_FLOW_INVALID: 7410 + default: 7411 + ASSERT(false); 7412 + } 7413 + } 7414 + 7415 + static void update_planes_and_stream_cleanup_v3_new_context( 7416 + struct dc_update_scratch_space *scratch 7417 + ) 7418 + { 7419 + swap_and_release_current_context(scratch->dc, scratch->new_context, scratch->stream); 7420 + } 7421 + 7422 + static void update_planes_and_stream_cleanup_v3_intermediate( 7423 + struct dc_update_scratch_space *scratch, 7424 + bool backup 7425 + ) 7426 + { 7427 + release_minimal_transition_state( 7428 + scratch->dc, 7429 + scratch->intermediate_context, 7430 + backup ? scratch->backup_context : scratch->new_context, 7431 + &scratch->intermediate_policy 7432 + ); 7433 + } 7434 + 7435 + static bool update_planes_and_stream_cleanup_v3( 7436 + struct dc_update_scratch_space *scratch 7437 + ) 7438 + { 7439 + switch (scratch->flow) { 7440 + case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST: 7441 + case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL: 7442 + // No cleanup required 7443 + break; 7444 + 7445 + case UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS: 7446 + update_planes_and_stream_cleanup_v3_new_context(scratch); 7447 + break; 7448 + 7449 + case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW: 7450 + update_planes_and_stream_cleanup_v3_intermediate(scratch, false); 7451 + update_planes_and_stream_cleanup_v3_new_context(scratch); 7452 + break; 7453 + 7454 + case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT: 7455 + swap_and_release_current_context(scratch->dc, scratch->intermediate_context, scratch->stream); 7456 + dc_state_retain(scratch->dc->current_state); 7457 + update_planes_and_stream_cleanup_v3_intermediate(scratch, true); 7458 + dc_state_release(scratch->backup_context); 7459 + restore_planes_and_stream_state(&scratch->dc->scratch.new_state, scratch->stream); 7460 + update_planes_and_stream_cleanup_v3_new_context(scratch); 7461 + break; 7462 + 7463 + case UPDATE_V3_FLOW_INVALID: 7464 + default: 7465 + ASSERT(false); 7466 + } 7467 + 7468 + if (scratch->do_clear_update_flags) 7469 + clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream); 7470 + 7471 + return false; 7472 + } 7473 + 7474 + struct dc_update_scratch_space *dc_update_planes_and_stream_init( 7475 + struct dc *dc, 7476 + struct dc_surface_update *surface_updates, 7477 + int surface_count, 7478 + struct dc_stream_state *stream, 7479 + struct dc_stream_update *stream_update 7480 + ) 7481 + { 7482 + const enum dce_version version = dc->ctx->dce_version; 7483 + struct dc_update_scratch_space *scratch = stream->update_scratch; 7484 + 7485 + *scratch = (struct dc_update_scratch_space){ 7486 + .dc = dc, 7487 + .surface_updates = surface_updates, 7488 + .surface_count = surface_count, 7489 + .stream = stream, 7490 + .stream_update = stream_update, 7491 + .update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21, 7492 + .do_clear_update_flags = version >= DCN_VERSION_3_2 || version == DCN_VERSION_3_01, 7493 + }; 7494 + 7495 + return scratch; 7496 + } 7497 + 7498 + bool dc_update_planes_and_stream_prepare( 7499 + struct dc_update_scratch_space *scratch 7500 + ) 7501 + { 7502 + return scratch->update_v3 7503 + ? update_planes_and_stream_prepare_v3(scratch) 7504 + : update_planes_and_stream_prepare_v2(scratch); 7505 + } 7506 + 7507 + void dc_update_planes_and_stream_execute( 7508 + const struct dc_update_scratch_space *scratch 7509 + ) 7510 + { 7511 + scratch->update_v3 7512 + ? update_planes_and_stream_execute_v3(scratch) 7513 + : update_planes_and_stream_execute_v2(scratch); 7514 + } 7515 + 7516 + bool dc_update_planes_and_stream_cleanup( 7517 + struct dc_update_scratch_space *scratch 7518 + ) 7519 + { 7520 + return scratch->update_v3 7521 + ? update_planes_and_stream_cleanup_v3(scratch) 7522 + : update_planes_and_stream_cleanup_v2(scratch); 7523 + } 7524 +
+24 -7
drivers/gpu/drm/amd/display/dc/core/dc_stream.c
··· 151 151 struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount); 152 152 153 153 dc_stream_destruct(stream); 154 + kfree(stream->update_scratch); 154 155 kfree(stream); 155 156 } 156 157 ··· 165 164 struct dc_stream_state *dc_create_stream_for_sink( 166 165 struct dc_sink *sink) 167 166 { 168 - struct dc_stream_state *stream; 167 + struct dc_stream_state *stream = NULL; 169 168 170 169 if (sink == NULL) 171 - return NULL; 170 + goto fail; 172 171 173 172 stream = kzalloc(sizeof(struct dc_stream_state), GFP_KERNEL); 174 173 if (stream == NULL) 175 - goto alloc_fail; 174 + goto fail; 175 + 176 + stream->update_scratch = kzalloc((int32_t) dc_update_scratch_space_size(), GFP_KERNEL); 177 + if (stream->update_scratch == NULL) 178 + goto fail; 176 179 177 180 if (dc_stream_construct(stream, sink) == false) 178 - goto construct_fail; 181 + goto fail; 179 182 180 183 kref_init(&stream->refcount); 181 184 182 185 return stream; 183 186 184 - construct_fail: 185 - kfree(stream); 187 + fail: 188 + if (stream) { 189 + kfree(stream->update_scratch); 190 + kfree(stream); 191 + } 186 192 187 - alloc_fail: 188 193 return NULL; 189 194 } 190 195 ··· 201 194 new_stream = kmemdup(stream, sizeof(struct dc_stream_state), GFP_KERNEL); 202 195 if (!new_stream) 203 196 return NULL; 197 + 198 + // Scratch is not meant to be reused across copies, as might have self-referential pointers 199 + new_stream->update_scratch = kzalloc( 200 + (int32_t) dc_update_scratch_space_size(), 201 + GFP_KERNEL 202 + ); 203 + if (!new_stream->update_scratch) { 204 + kfree(new_stream); 205 + return NULL; 206 + } 204 207 205 208 if (new_stream->sink) 206 209 dc_sink_retain(new_stream->sink);
+30 -1
drivers/gpu/drm/amd/display/dc/dc_stream.h
··· 315 315 struct luminance_data lumin_data; 316 316 bool scaler_sharpener_update; 317 317 bool sharpening_required; 318 + 319 + struct dc_update_scratch_space *update_scratch; 318 320 }; 319 321 320 322 #define ABM_LEVEL_IMMEDIATE_DISABLE 255 ··· 391 389 struct dc_surface_update *surface_updates, int surface_count, 392 390 struct dc_stream_state *dc_stream, 393 391 struct dc_stream_update *stream_update); 392 + 393 + struct dc_update_scratch_space; 394 + 395 + size_t dc_update_scratch_space_size(void); 396 + 397 + struct dc_update_scratch_space *dc_update_planes_and_stream_init( 398 + struct dc *dc, 399 + struct dc_surface_update *surface_updates, 400 + int surface_count, 401 + struct dc_stream_state *dc_stream, 402 + struct dc_stream_update *stream_update 403 + ); 404 + 405 + // Locked, false is failed 406 + bool dc_update_planes_and_stream_prepare( 407 + struct dc_update_scratch_space *scratch 408 + ); 409 + 410 + // Unlocked 411 + void dc_update_planes_and_stream_execute( 412 + const struct dc_update_scratch_space *scratch 413 + ); 414 + 415 + // Locked, true if call again 416 + bool dc_update_planes_and_stream_cleanup( 417 + struct dc_update_scratch_space *scratch 418 + ); 394 419 395 420 /* 396 421 * Set up surface attributes and associate to a stream ··· 626 597 void dc_dmub_update_dirty_rect(struct dc *dc, 627 598 int surface_count, 628 599 struct dc_stream_state *stream, 629 - struct dc_surface_update *srf_updates, 600 + const struct dc_surface_update *srf_updates, 630 601 struct dc_state *context); 631 602 632 603 bool dc_stream_is_cursor_limit_pending(struct dc *dc, struct dc_stream_state *stream);