#include "dml2_dc_types.h"
#include "dml2_internal_types.h"
#include "dml2_utils.h"
#include "dml2_mall_phantom.h"
unsigned int dml2_helper_calculate_num_ways_for_subvp(struct dml2_context *ctx, struct dc_state *context)
{
uint32_t num_ways = 0;
uint32_t bytes_per_pixel = 0;
uint32_t cache_lines_used = 0;
uint32_t lines_per_way = 0;
uint32_t total_cache_lines = 0;
uint32_t bytes_in_mall = 0;
uint32_t num_mblks = 0;
uint32_t cache_lines_per_plane = 0;
uint32_t i = 0;
uint32_t mblk_width = 0;
uint32_t mblk_height = 0;
uint32_t full_vp_width_blk_aligned = 0;
uint32_t mall_alloc_width_blk_aligned = 0;
uint32_t mall_alloc_height_blk_aligned = 0;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->stream && pipe->plane_state && !pipe->top_pipe && !pipe->prev_odm_pipe &&
ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
bytes_per_pixel = pipe->plane_state->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 ? 8 : 4;
mblk_width = ctx->config.mall_cfg.mblk_width_pixels;
mblk_height = bytes_per_pixel == 4 ? mblk_width = ctx->config.mall_cfg.mblk_height_4bpe_pixels : ctx->config.mall_cfg.mblk_height_8bpe_pixels;
full_vp_width_blk_aligned = ((pipe->plane_res.scl_data.viewport.x +
pipe->plane_res.scl_data.viewport.width + mblk_width - 1) / mblk_width * mblk_width) +
(pipe->plane_res.scl_data.viewport.x / mblk_width * mblk_width);
mall_alloc_width_blk_aligned = full_vp_width_blk_aligned;
mall_alloc_height_blk_aligned = (pipe->stream->timing.v_addressable - 1 + mblk_height - 1) /
mblk_height * mblk_height + mblk_height;
num_mblks = ((mall_alloc_width_blk_aligned + mblk_width - 1) / mblk_width) *
((mall_alloc_height_blk_aligned + mblk_height - 1) / mblk_height);
bytes_in_mall = num_mblks * ctx->config.mall_cfg.mblk_size_bytes;
cache_lines_per_plane = bytes_in_mall / ctx->config.mall_cfg.cache_line_size_bytes + 2;
if (pipe->plane_state->dcc.enable)
cache_lines_per_plane *= 2;
cache_lines_used += cache_lines_per_plane;
}
}
total_cache_lines = ctx->config.mall_cfg.max_cab_allocation_bytes / ctx->config.mall_cfg.cache_line_size_bytes;
lines_per_way = total_cache_lines / ctx->config.mall_cfg.cache_num_ways;
num_ways = cache_lines_used / lines_per_way;
if (cache_lines_used % lines_per_way > 0)
num_ways++;
return num_ways;
}
static void merge_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *context)
{
int i;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->prev_odm_pipe) {
pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe;
if (pipe->next_odm_pipe)
pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe;
pipe->bottom_pipe = NULL;
pipe->next_odm_pipe = NULL;
pipe->plane_state = NULL;
pipe->stream = NULL;
pipe->top_pipe = NULL;
pipe->prev_odm_pipe = NULL;
if (pipe->stream_res.dsc)
ctx->config.svp_pstate.callbacks.release_dsc(&context->res_ctx, ctx->config.svp_pstate.callbacks.dc->res_pool, &pipe->stream_res.dsc);
memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
} else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) {
struct pipe_ctx *top_pipe = pipe->top_pipe;
struct pipe_ctx *bottom_pipe = pipe->bottom_pipe;
top_pipe->bottom_pipe = bottom_pipe;
if (bottom_pipe)
bottom_pipe->top_pipe = top_pipe;
pipe->top_pipe = NULL;
pipe->bottom_pipe = NULL;
pipe->plane_state = NULL;
pipe->stream = NULL;
memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
}
}
}
static bool all_pipes_have_stream_and_plane(struct dml2_context *ctx, const struct dc_state *context)
{
int i;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->stream)
continue;
if (!pipe->plane_state)
return false;
}
return true;
}
static bool mpo_in_use(const struct dc_state *context)
{
int i;
for (i = 0; i < context->stream_count; i++) {
if (context->stream_status[i].plane_count > 1)
return true;
}
return false;
}
static unsigned int get_num_free_pipes(struct dml2_context *ctx, struct dc_state *state)
{
unsigned int i;
unsigned int free_pipes = 0;
unsigned int num_pipes = 0;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
if (pipe->stream && !pipe->top_pipe) {
while (pipe) {
num_pipes++;
pipe = pipe->bottom_pipe;
}
}
}
free_pipes = ctx->config.dcn_pipe_count - num_pipes;
return free_pipes;
}
static bool assign_subvp_pipe(struct dml2_context *ctx, struct dc_state *context, unsigned int *index)
{
unsigned int i, pipe_idx;
unsigned int max_frame_time = 0;
bool valid_assignment_found = false;
unsigned int free_pipes = 2;
bool current_assignment_freesync = false;
struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
unsigned int num_pipes = 0;
unsigned int refresh_rate = 0;
if (!pipe->stream)
continue;
refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +
pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)
/ (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);
if (pipe->plane_state && !pipe->top_pipe &&
ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_NONE && refresh_rate < 120 &&
vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) {
while (pipe) {
num_pipes++;
pipe = pipe->bottom_pipe;
}
pipe = &context->res_ctx.pipe_ctx[i];
if (num_pipes <= free_pipes) {
struct dc_stream_state *stream = pipe->stream;
unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /
(double)(stream->timing.pix_clk_100hz * 100)) * 1000000;
if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) {
*index = i;
max_frame_time = frame_us;
valid_assignment_found = true;
current_assignment_freesync = false;
} else if (stream->ignore_msa_timing_param && (!valid_assignment_found ||
(current_assignment_freesync && frame_us > max_frame_time))) {
*index = i;
valid_assignment_found = true;
current_assignment_freesync = true;
}
}
}
pipe_idx++;
}
return valid_assignment_found;
}
static bool enough_pipes_for_subvp(struct dml2_context *ctx, struct dc_state *state)
{
unsigned int i, split_cnt, free_pipes;
unsigned int min_pipe_split = ctx->config.dcn_pipe_count + 1;
bool subvp_possible = false;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
if (pipe->stream && !pipe->top_pipe &&
ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_NONE) {
split_cnt = 0;
while (pipe) {
split_cnt++;
pipe = pipe->bottom_pipe;
}
if (split_cnt < min_pipe_split)
min_pipe_split = split_cnt;
}
}
free_pipes = get_num_free_pipes(ctx, state);
if (free_pipes >= min_pipe_split && free_pipes < ctx->config.dcn_pipe_count)
subvp_possible = true;
return subvp_possible;
}
static bool subvp_subvp_schedulable(struct dml2_context *ctx, struct dc_state *context)
{
struct pipe_ctx *subvp_pipes[2];
struct dc_stream_state *phantom = NULL;
uint32_t microschedule_lines = 0;
uint32_t index = 0;
uint32_t i;
uint32_t max_microschedule_us = 0;
int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
uint32_t time_us = 0;
if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) {
phantom = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream);
microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) +
phantom->timing.v_addressable;
time_us = (microschedule_lines * phantom->timing.h_total) /
(double)(phantom->timing.pix_clk_100hz * 100) * 1000000 +
ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us +
ctx->config.svp_pstate.subvp_fw_processing_delay_us + 1;
if (time_us > max_microschedule_us)
max_microschedule_us = time_us;
subvp_pipes[index] = pipe;
index++;
if (index == 2)
break;
}
}
vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) /
(double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) /
(double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) *
subvp_pipes[0]->stream->timing.h_total) /
(double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) *
subvp_pipes[1]->stream->timing.h_total) /
(double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us &&
(vactive2_us - vblank1_us) / 2 > max_microschedule_us)
return true;
return false;
}
bool dml2_svp_drr_schedulable(struct dml2_context *ctx, struct dc_state *context, struct dc_crtc_timing *drr_timing)
{
bool schedulable = false;
uint32_t i;
struct pipe_ctx *pipe = NULL;
struct dc_crtc_timing *main_timing = NULL;
struct dc_crtc_timing *phantom_timing = NULL;
struct dc_stream_state *phantom_stream;
int16_t prefetch_us = 0;
int16_t mall_region_us = 0;
int16_t drr_frame_us = 0;
int16_t subvp_active_us = 0;
int16_t stretched_drr_us = 0;
int16_t drr_stretched_vblank_us = 0;
int16_t max_vblank_mallregion = 0;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
continue;
if (ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe) == SUBVP_MAIN)
break;
}
phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, pipe->stream);
main_timing = &pipe->stream->timing;
phantom_timing = &phantom_stream->timing;
prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
(double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us;
subvp_active_us = main_timing->v_addressable * main_timing->h_total /
(double)(main_timing->pix_clk_100hz * 100) * 1000000;
drr_frame_us = drr_timing->v_total * drr_timing->h_total /
(double)(drr_timing->pix_clk_100hz * 100) * 1000000;
mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
(double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;
drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total /
(double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us);
max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us;
if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 &&
subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0)
schedulable = true;
return schedulable;
}
static bool subvp_vblank_schedulable(struct dml2_context *ctx, struct dc_state *context)
{
struct pipe_ctx *pipe = NULL;
struct pipe_ctx *subvp_pipe = NULL;
bool found = false;
bool schedulable = false;
uint32_t i = 0;
uint8_t vblank_index = 0;
uint16_t prefetch_us = 0;
uint16_t mall_region_us = 0;
uint16_t vblank_frame_us = 0;
uint16_t subvp_active_us = 0;
uint16_t vblank_blank_us = 0;
uint16_t max_vblank_mallregion = 0;
struct dc_crtc_timing *main_timing = NULL;
struct dc_crtc_timing *phantom_timing = NULL;
struct dc_crtc_timing *vblank_timing = NULL;
struct dc_stream_state *phantom_stream;
enum mall_stream_type pipe_mall_type;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe);
if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
continue;
if (!found && pipe_mall_type == SUBVP_NONE) {
vblank_index = i;
found = true;
}
if (!subvp_pipe && pipe_mall_type == SUBVP_MAIN)
subvp_pipe = pipe;
}
if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) {
schedulable = dml2_svp_drr_schedulable(ctx, context, &context->res_ctx.pipe_ctx[vblank_index].stream->timing);
} else if (found) {
phantom_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(context, subvp_pipe->stream);
main_timing = &subvp_pipe->stream->timing;
phantom_timing = &phantom_stream->timing;
vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;
prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
(double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
ctx->config.svp_pstate.subvp_prefetch_end_to_mall_start_us;
mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
(double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total /
(double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
vblank_blank_us = (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total /
(double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
subvp_active_us = main_timing->v_addressable * main_timing->h_total /
(double)(main_timing->pix_clk_100hz * 100) * 1000000;
max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us;
if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0)
schedulable = true;
}
return schedulable;
}
bool dml2_svp_validate_static_schedulability(struct dml2_context *ctx, struct dc_state *context, enum dml_dram_clock_change_support pstate_change_type)
{
bool schedulable = true;
struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
uint32_t i, pipe_idx;
uint8_t subvp_count = 0;
uint8_t vactive_count = 0;
for (i = 0, pipe_idx = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
enum mall_stream_type pipe_mall_type = ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(context, pipe);
if (!pipe->stream)
continue;
if (pipe->plane_state && !pipe->top_pipe &&
pipe_mall_type == SUBVP_MAIN)
subvp_count++;
if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 &&
pipe_mall_type == SUBVP_NONE) {
vactive_count++;
}
pipe_idx++;
}
if (subvp_count == 2) {
schedulable = subvp_subvp_schedulable(ctx, context);
} else if (pstate_change_type == dml_dram_clock_change_vblank_w_mall_sub_vp) {
if (vactive_count > 0)
schedulable = false;
else
schedulable = subvp_vblank_schedulable(ctx, context);
} else if (pstate_change_type == dml_dram_clock_change_vactive_w_mall_sub_vp &&
vactive_count > 0) {
schedulable = false;
}
return schedulable;
}
static void set_phantom_stream_timing(struct dml2_context *ctx, struct dc_state *state,
struct pipe_ctx *ref_pipe,
struct dc_stream_state *phantom_stream,
unsigned int dc_pipe_idx,
unsigned int svp_height,
unsigned int svp_vstartup)
{
unsigned int i;
double line_time, fp_and_sync_width_time;
struct pipe_ctx *pipe;
uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines;
static const double cvt_rb_vblank_max = ((double) 460 / (1000 * 1000));
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
pipe = &state->res_ctx.pipe_ctx[i];
if (!pipe->stream)
continue;
if (i == dc_pipe_idx)
break;
}
pstate_width_fw_delay_lines = ((double)(ctx->config.svp_pstate.subvp_fw_processing_delay_us +
ctx->config.svp_pstate.subvp_pstate_allow_width_us) / 1000000) *
(ref_pipe->stream->timing.pix_clk_100hz * 100) /
(double)ref_pipe->stream->timing.h_total;
phantom_vactive = svp_height + pstate_width_fw_delay_lines + ctx->config.svp_pstate.subvp_swath_height_margin_lines;
phantom_stream->timing.v_front_porch = 1;
line_time = phantom_stream->timing.h_total / ((double)phantom_stream->timing.pix_clk_100hz * 100);
fp_and_sync_width_time = (phantom_stream->timing.v_front_porch + phantom_stream->timing.v_sync_width) * line_time;
if ((svp_vstartup * line_time) + fp_and_sync_width_time > cvt_rb_vblank_max) {
svp_vstartup = (cvt_rb_vblank_max - fp_and_sync_width_time) / line_time;
}
phantom_bp = svp_vstartup;
phantom_stream->dst.y = 0;
phantom_stream->dst.height = phantom_vactive;
phantom_stream->src.y = 0;
phantom_stream->src.height = phantom_vactive;
phantom_stream->timing.v_addressable = phantom_vactive;
phantom_stream->timing.v_total = phantom_stream->timing.v_addressable +
phantom_stream->timing.v_front_porch +
phantom_stream->timing.v_sync_width +
phantom_bp;
phantom_stream->timing.flags.DSC = 0;
}
static struct dc_stream_state *enable_phantom_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int dc_pipe_idx, unsigned int svp_height, unsigned int vstartup)
{
struct pipe_ctx *ref_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx];
struct dc_stream_state *phantom_stream = ctx->config.svp_pstate.callbacks.create_phantom_stream(
ctx->config.svp_pstate.callbacks.dc,
state,
ref_pipe->stream);
memcpy(&phantom_stream->timing, &ref_pipe->stream->timing, sizeof(phantom_stream->timing));
memcpy(&phantom_stream->src, &ref_pipe->stream->src, sizeof(phantom_stream->src));
memcpy(&phantom_stream->dst, &ref_pipe->stream->dst, sizeof(phantom_stream->dst));
set_phantom_stream_timing(ctx, state, ref_pipe, phantom_stream, dc_pipe_idx, svp_height, vstartup);
ctx->config.svp_pstate.callbacks.add_phantom_stream(ctx->config.svp_pstate.callbacks.dc,
state,
phantom_stream,
ref_pipe->stream);
return phantom_stream;
}
static void enable_phantom_plane(struct dml2_context *ctx,
struct dc_state *state,
struct dc_stream_state *phantom_stream,
unsigned int dc_pipe_idx)
{
struct dc_plane_state *phantom_plane = NULL;
struct dc_plane_state *prev_phantom_plane = NULL;
struct pipe_ctx *curr_pipe = &state->res_ctx.pipe_ctx[dc_pipe_idx];
while (curr_pipe) {
if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state) {
phantom_plane = prev_phantom_plane;
} else {
phantom_plane = ctx->config.svp_pstate.callbacks.create_phantom_plane(
ctx->config.svp_pstate.callbacks.dc,
state,
curr_pipe->plane_state);
if (!phantom_plane)
return;
}
memcpy(&phantom_plane->address, &curr_pipe->plane_state->address, sizeof(phantom_plane->address));
memcpy(&phantom_plane->scaling_quality, &curr_pipe->plane_state->scaling_quality,
sizeof(phantom_plane->scaling_quality));
memcpy(&phantom_plane->src_rect, &curr_pipe->plane_state->src_rect, sizeof(phantom_plane->src_rect));
memcpy(&phantom_plane->dst_rect, &curr_pipe->plane_state->dst_rect, sizeof(phantom_plane->dst_rect));
memcpy(&phantom_plane->clip_rect, &curr_pipe->plane_state->clip_rect, sizeof(phantom_plane->clip_rect));
memcpy(&phantom_plane->plane_size, &curr_pipe->plane_state->plane_size,
sizeof(phantom_plane->plane_size));
memcpy(&phantom_plane->tiling_info, &curr_pipe->plane_state->tiling_info,
sizeof(phantom_plane->tiling_info));
memcpy(&phantom_plane->dcc, &curr_pipe->plane_state->dcc, sizeof(phantom_plane->dcc));
phantom_plane->format = curr_pipe->plane_state->format;
phantom_plane->rotation = curr_pipe->plane_state->rotation;
phantom_plane->visible = curr_pipe->plane_state->visible;
phantom_plane->clip_rect.y = 0;
phantom_plane->clip_rect.height = phantom_stream->timing.v_addressable;
ctx->config.svp_pstate.callbacks.add_phantom_plane(ctx->config.svp_pstate.callbacks.dc, phantom_stream, phantom_plane, state);
curr_pipe = curr_pipe->bottom_pipe;
prev_phantom_plane = phantom_plane;
}
}
static void add_phantom_pipes_for_main_pipe(struct dml2_context *ctx, struct dc_state *state, unsigned int main_pipe_idx, unsigned int svp_height, unsigned int vstartup)
{
struct dc_stream_state *phantom_stream = NULL;
unsigned int i;
phantom_stream = enable_phantom_stream(ctx, state, main_pipe_idx, svp_height, vstartup);
enable_phantom_plane(ctx, state, phantom_stream, main_pipe_idx);
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
if (pipe->plane_state && pipe->stream && pipe->stream == phantom_stream &&
ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) {
pipe->stream->use_dynamic_meta = false;
pipe->plane_state->flip_immediate = false;
if (!ctx->config.svp_pstate.callbacks.build_scaling_params(pipe)) {
}
}
}
}
static bool remove_all_phantom_planes_for_stream(struct dml2_context *ctx, struct dc_stream_state *stream, struct dc_state *context)
{
int i, old_plane_count;
struct dc_stream_status *stream_status = NULL;
struct dc_plane_state *del_planes[MAX_SURFACES] = { 0 };
for (i = 0; i < context->stream_count; i++)
if (context->streams[i] == stream) {
stream_status = &context->stream_status[i];
break;
}
if (stream_status == NULL) {
return false;
}
old_plane_count = stream_status->plane_count;
for (i = 0; i < old_plane_count; i++)
del_planes[i] = stream_status->plane_states[i];
for (i = 0; i < old_plane_count; i++) {
if (!ctx->config.svp_pstate.callbacks.remove_phantom_plane(ctx->config.svp_pstate.callbacks.dc, stream, del_planes[i], context))
return false;
ctx->config.svp_pstate.callbacks.release_phantom_plane(ctx->config.svp_pstate.callbacks.dc, context, del_planes[i]);
}
return true;
}
bool dml2_svp_remove_all_phantom_pipes(struct dml2_context *ctx, struct dc_state *state)
{
int i;
bool removed_pipe = false;
struct dc_stream_state *phantom_stream = NULL;
for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
if (pipe->plane_state && pipe->stream && ctx->config.svp_pstate.callbacks.get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) {
phantom_stream = pipe->stream;
remove_all_phantom_planes_for_stream(ctx, phantom_stream, state);
ctx->config.svp_pstate.callbacks.remove_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream);
ctx->config.svp_pstate.callbacks.release_phantom_stream(ctx->config.svp_pstate.callbacks.dc, state, phantom_stream);
removed_pipe = true;
}
if (pipe->plane_state) {
pipe->plane_state->is_phantom = false;
}
}
return removed_pipe;
}
bool dml2_svp_add_phantom_pipe_to_dc_state(struct dml2_context *ctx, struct dc_state *state, struct dml_mode_support_info_st *mode_support_info)
{
unsigned int dc_pipe_idx, dml_pipe_idx;
unsigned int svp_height, vstartup;
if (ctx->config.svp_pstate.force_disable_subvp)
return false;
if (!all_pipes_have_stream_and_plane(ctx, state))
return false;
if (mpo_in_use(state))
return false;
merge_pipes_for_subvp(ctx, state);
for (int i = 0; i < ctx->config.dcn_pipe_count; i++) {
struct pipe_ctx *pipe_ctx = &state->res_ctx.pipe_ctx[i];
if (!pipe_ctx->plane_state || !pipe_ctx->stream)
continue;
ctx->config.svp_pstate.callbacks.build_scaling_params(pipe_ctx);
}
if (enough_pipes_for_subvp(ctx, state) && assign_subvp_pipe(ctx, state, &dc_pipe_idx)) {
dml_pipe_idx = dml2_helper_find_dml_pipe_idx_by_stream_id(ctx, state->res_ctx.pipe_ctx[dc_pipe_idx].stream->stream_id);
svp_height = mode_support_info->SubViewportLinesNeededInMALL[dml_pipe_idx];
vstartup = dml_get_vstartup_calculated(&ctx->v20.dml_core_ctx, dml_pipe_idx);
add_phantom_pipes_for_main_pipe(ctx, state, dc_pipe_idx, svp_height, vstartup);
return true;
}
return false;
}