881 lines
23 KiB
C
881 lines
23 KiB
C
/*
|
|
* Copyright 2023 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 "core_types.h"
|
|
#include "core_status.h"
|
|
#include "dc_state.h"
|
|
#include "dc_state_priv.h"
|
|
#include "dc_stream_priv.h"
|
|
#include "dc_plane_priv.h"
|
|
|
|
#include "dm_services.h"
|
|
#include "resource.h"
|
|
#include "link_enc_cfg.h"
|
|
|
|
#include "dml2/dml2_wrapper.h"
|
|
#include "dml2/dml2_internal_types.h"
|
|
|
|
#define DC_LOGGER \
|
|
dc->ctx->logger
|
|
#define DC_LOGGER_INIT(logger)
|
|
|
|
/* Private dc_state helper functions */
|
|
static bool dc_state_track_phantom_stream(struct dc_state *state,
|
|
struct dc_stream_state *phantom_stream)
|
|
{
|
|
if (state->phantom_stream_count >= MAX_PHANTOM_PIPES)
|
|
return false;
|
|
|
|
state->phantom_streams[state->phantom_stream_count++] = phantom_stream;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool dc_state_untrack_phantom_stream(struct dc_state *state, struct dc_stream_state *phantom_stream)
|
|
{
|
|
bool res = false;
|
|
int i;
|
|
|
|
/* first find phantom stream in the dc_state */
|
|
for (i = 0; i < state->phantom_stream_count; i++) {
|
|
if (state->phantom_streams[i] == phantom_stream) {
|
|
state->phantom_streams[i] = NULL;
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* failed to find stream in state */
|
|
if (!res)
|
|
return res;
|
|
|
|
/* trim back phantom streams */
|
|
state->phantom_stream_count--;
|
|
for (; i < state->phantom_stream_count; i++)
|
|
state->phantom_streams[i] = state->phantom_streams[i + 1];
|
|
|
|
return res;
|
|
}
|
|
|
|
static bool dc_state_is_phantom_stream_tracked(struct dc_state *state, struct dc_stream_state *phantom_stream)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < state->phantom_stream_count; i++) {
|
|
if (state->phantom_streams[i] == phantom_stream)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool dc_state_track_phantom_plane(struct dc_state *state,
|
|
struct dc_plane_state *phantom_plane)
|
|
{
|
|
if (state->phantom_plane_count >= MAX_PHANTOM_PIPES)
|
|
return false;
|
|
|
|
state->phantom_planes[state->phantom_plane_count++] = phantom_plane;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool dc_state_untrack_phantom_plane(struct dc_state *state, struct dc_plane_state *phantom_plane)
|
|
{
|
|
bool res = false;
|
|
int i;
|
|
|
|
/* first find phantom plane in the dc_state */
|
|
for (i = 0; i < state->phantom_plane_count; i++) {
|
|
if (state->phantom_planes[i] == phantom_plane) {
|
|
state->phantom_planes[i] = NULL;
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* failed to find plane in state */
|
|
if (!res)
|
|
return res;
|
|
|
|
/* trim back phantom planes */
|
|
state->phantom_plane_count--;
|
|
for (; i < state->phantom_plane_count; i++)
|
|
state->phantom_planes[i] = state->phantom_planes[i + 1];
|
|
|
|
return res;
|
|
}
|
|
|
|
static bool dc_state_is_phantom_plane_tracked(struct dc_state *state, struct dc_plane_state *phantom_plane)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < state->phantom_plane_count; i++) {
|
|
if (state->phantom_planes[i] == phantom_plane)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void dc_state_copy_internal(struct dc_state *dst_state, struct dc_state *src_state)
|
|
{
|
|
int i, j;
|
|
|
|
memcpy(dst_state, src_state, sizeof(struct dc_state));
|
|
|
|
for (i = 0; i < MAX_PIPES; i++) {
|
|
struct pipe_ctx *cur_pipe = &dst_state->res_ctx.pipe_ctx[i];
|
|
|
|
if (cur_pipe->top_pipe)
|
|
cur_pipe->top_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->top_pipe->pipe_idx];
|
|
|
|
if (cur_pipe->bottom_pipe)
|
|
cur_pipe->bottom_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->bottom_pipe->pipe_idx];
|
|
|
|
if (cur_pipe->prev_odm_pipe)
|
|
cur_pipe->prev_odm_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->prev_odm_pipe->pipe_idx];
|
|
|
|
if (cur_pipe->next_odm_pipe)
|
|
cur_pipe->next_odm_pipe = &dst_state->res_ctx.pipe_ctx[cur_pipe->next_odm_pipe->pipe_idx];
|
|
}
|
|
|
|
/* retain phantoms */
|
|
for (i = 0; i < dst_state->phantom_stream_count; i++)
|
|
dc_stream_retain(dst_state->phantom_streams[i]);
|
|
|
|
for (i = 0; i < dst_state->phantom_plane_count; i++)
|
|
dc_plane_state_retain(dst_state->phantom_planes[i]);
|
|
|
|
/* retain streams and planes */
|
|
for (i = 0; i < dst_state->stream_count; i++) {
|
|
dc_stream_retain(dst_state->streams[i]);
|
|
for (j = 0; j < dst_state->stream_status[i].plane_count; j++)
|
|
dc_plane_state_retain(
|
|
dst_state->stream_status[i].plane_states[j]);
|
|
}
|
|
|
|
}
|
|
|
|
static void init_state(struct dc *dc, struct dc_state *state)
|
|
{
|
|
/* Each context must have their own instance of VBA and in order to
|
|
* initialize and obtain IP and SOC the base DML instance from DC is
|
|
* initially copied into every context
|
|
*/
|
|
memcpy(&state->bw_ctx.dml, &dc->dml, sizeof(struct display_mode_lib));
|
|
}
|
|
|
|
/* Public dc_state functions */
|
|
struct dc_state *dc_state_create(struct dc *dc)
|
|
{
|
|
struct dc_state *state = kvzalloc(sizeof(struct dc_state),
|
|
GFP_KERNEL);
|
|
|
|
if (!state)
|
|
return NULL;
|
|
|
|
init_state(dc, state);
|
|
dc_state_construct(dc, state);
|
|
|
|
#ifdef CONFIG_DRM_AMD_DC_FP
|
|
if (dc->debug.using_dml2)
|
|
dml2_create(dc, &dc->dml2_options, &state->bw_ctx.dml2);
|
|
#endif
|
|
|
|
kref_init(&state->refcount);
|
|
|
|
return state;
|
|
}
|
|
|
|
void dc_state_copy(struct dc_state *dst_state, struct dc_state *src_state)
|
|
{
|
|
struct kref refcount = dst_state->refcount;
|
|
#ifdef CONFIG_DRM_AMD_DC_FP
|
|
struct dml2_context *dst_dml2 = dst_state->bw_ctx.dml2;
|
|
#endif
|
|
|
|
dc_state_copy_internal(dst_state, src_state);
|
|
|
|
#ifdef CONFIG_DRM_AMD_DC_FP
|
|
dst_state->bw_ctx.dml2 = dst_dml2;
|
|
if (src_state->bw_ctx.dml2)
|
|
dml2_copy(dst_state->bw_ctx.dml2, src_state->bw_ctx.dml2);
|
|
#endif
|
|
|
|
/* context refcount should not be overridden */
|
|
dst_state->refcount = refcount;
|
|
}
|
|
|
|
struct dc_state *dc_state_create_copy(struct dc_state *src_state)
|
|
{
|
|
struct dc_state *new_state;
|
|
|
|
new_state = kvmalloc(sizeof(struct dc_state),
|
|
GFP_KERNEL);
|
|
if (!new_state)
|
|
return NULL;
|
|
|
|
dc_state_copy_internal(new_state, src_state);
|
|
|
|
#ifdef CONFIG_DRM_AMD_DC_FP
|
|
if (src_state->bw_ctx.dml2 &&
|
|
!dml2_create_copy(&new_state->bw_ctx.dml2, src_state->bw_ctx.dml2)) {
|
|
dc_state_release(new_state);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
kref_init(&new_state->refcount);
|
|
|
|
return new_state;
|
|
}
|
|
|
|
void dc_state_copy_current(struct dc *dc, struct dc_state *dst_state)
|
|
{
|
|
dc_state_copy(dst_state, dc->current_state);
|
|
}
|
|
|
|
struct dc_state *dc_state_create_current_copy(struct dc *dc)
|
|
{
|
|
return dc_state_create_copy(dc->current_state);
|
|
}
|
|
|
|
void dc_state_construct(struct dc *dc, struct dc_state *state)
|
|
{
|
|
state->clk_mgr = dc->clk_mgr;
|
|
|
|
/* Initialise DIG link encoder resource tracking variables. */
|
|
if (dc->res_pool)
|
|
link_enc_cfg_init(dc, state);
|
|
}
|
|
|
|
void dc_state_destruct(struct dc_state *state)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < state->stream_count; i++) {
|
|
for (j = 0; j < state->stream_status[i].plane_count; j++)
|
|
dc_plane_state_release(
|
|
state->stream_status[i].plane_states[j]);
|
|
|
|
state->stream_status[i].plane_count = 0;
|
|
dc_stream_release(state->streams[i]);
|
|
state->streams[i] = NULL;
|
|
}
|
|
state->stream_count = 0;
|
|
|
|
/* release tracked phantoms */
|
|
for (i = 0; i < state->phantom_stream_count; i++) {
|
|
dc_stream_release(state->phantom_streams[i]);
|
|
state->phantom_streams[i] = NULL;
|
|
}
|
|
state->phantom_stream_count = 0;
|
|
|
|
for (i = 0; i < state->phantom_plane_count; i++) {
|
|
dc_plane_state_release(state->phantom_planes[i]);
|
|
state->phantom_planes[i] = NULL;
|
|
}
|
|
state->phantom_plane_count = 0;
|
|
|
|
state->stream_mask = 0;
|
|
memset(&state->res_ctx, 0, sizeof(state->res_ctx));
|
|
memset(&state->pp_display_cfg, 0, sizeof(state->pp_display_cfg));
|
|
memset(&state->dcn_bw_vars, 0, sizeof(state->dcn_bw_vars));
|
|
state->clk_mgr = NULL;
|
|
memset(&state->bw_ctx.bw, 0, sizeof(state->bw_ctx.bw));
|
|
memset(state->block_sequence, 0, sizeof(state->block_sequence));
|
|
state->block_sequence_steps = 0;
|
|
memset(state->dc_dmub_cmd, 0, sizeof(state->dc_dmub_cmd));
|
|
state->dmub_cmd_count = 0;
|
|
memset(&state->perf_params, 0, sizeof(state->perf_params));
|
|
memset(&state->scratch, 0, sizeof(state->scratch));
|
|
}
|
|
|
|
void dc_state_retain(struct dc_state *state)
|
|
{
|
|
kref_get(&state->refcount);
|
|
}
|
|
|
|
static void dc_state_free(struct kref *kref)
|
|
{
|
|
struct dc_state *state = container_of(kref, struct dc_state, refcount);
|
|
|
|
dc_state_destruct(state);
|
|
|
|
#ifdef CONFIG_DRM_AMD_DC_FP
|
|
dml2_destroy(state->bw_ctx.dml2);
|
|
state->bw_ctx.dml2 = 0;
|
|
#endif
|
|
|
|
kvfree(state);
|
|
}
|
|
|
|
void dc_state_release(struct dc_state *state)
|
|
{
|
|
if (state != NULL)
|
|
kref_put(&state->refcount, dc_state_free);
|
|
}
|
|
/*
|
|
* dc_state_add_stream() - Add a new dc_stream_state to a dc_state.
|
|
*/
|
|
enum dc_status dc_state_add_stream(
|
|
struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *stream)
|
|
{
|
|
enum dc_status res;
|
|
|
|
DC_LOGGER_INIT(dc->ctx->logger);
|
|
|
|
if (state->stream_count >= dc->res_pool->timing_generator_count) {
|
|
DC_LOG_WARNING("Max streams reached, can't add stream %p !\n", stream);
|
|
return DC_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
state->streams[state->stream_count] = stream;
|
|
dc_stream_retain(stream);
|
|
state->stream_count++;
|
|
|
|
res = resource_add_otg_master_for_stream_output(
|
|
state, dc->res_pool, stream);
|
|
if (res != DC_OK)
|
|
DC_LOG_WARNING("Adding stream %p to context failed with err %d!\n", stream, res);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* dc_state_remove_stream() - Remove a stream from a dc_state.
|
|
*/
|
|
enum dc_status dc_state_remove_stream(
|
|
struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *stream)
|
|
{
|
|
int i;
|
|
struct pipe_ctx *del_pipe = resource_get_otg_master_for_stream(
|
|
&state->res_ctx, stream);
|
|
|
|
if (!del_pipe) {
|
|
dm_error("Pipe not found for stream %p !\n", stream);
|
|
return DC_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
resource_update_pipes_for_stream_with_slice_count(state,
|
|
dc->current_state, dc->res_pool, stream, 1);
|
|
resource_remove_otg_master_for_stream_output(
|
|
state, dc->res_pool, stream);
|
|
|
|
for (i = 0; i < state->stream_count; i++)
|
|
if (state->streams[i] == stream)
|
|
break;
|
|
|
|
if (state->streams[i] != stream) {
|
|
dm_error("Context doesn't have stream %p !\n", stream);
|
|
return DC_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
dc_stream_release(state->streams[i]);
|
|
state->stream_count--;
|
|
|
|
/* Trim back arrays */
|
|
for (; i < state->stream_count; i++) {
|
|
state->streams[i] = state->streams[i + 1];
|
|
state->stream_status[i] = state->stream_status[i + 1];
|
|
}
|
|
|
|
state->streams[state->stream_count] = NULL;
|
|
memset(
|
|
&state->stream_status[state->stream_count],
|
|
0,
|
|
sizeof(state->stream_status[0]));
|
|
|
|
return DC_OK;
|
|
}
|
|
|
|
bool dc_state_add_plane(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *stream,
|
|
struct dc_plane_state *plane_state,
|
|
struct dc_state *state)
|
|
{
|
|
struct resource_pool *pool = dc->res_pool;
|
|
struct pipe_ctx *otg_master_pipe;
|
|
struct dc_stream_status *stream_status = NULL;
|
|
bool added = false;
|
|
|
|
stream_status = dc_state_get_stream_status(state, stream);
|
|
if (stream_status == NULL) {
|
|
dm_error("Existing stream not found; failed to attach surface!\n");
|
|
goto out;
|
|
} else if (stream_status->plane_count == MAX_SURFACE_NUM) {
|
|
dm_error("Surface: can not attach plane_state %p! Maximum is: %d\n",
|
|
plane_state, MAX_SURFACE_NUM);
|
|
goto out;
|
|
}
|
|
|
|
if (stream_status->plane_count == 0 && dc->config.enable_windowed_mpo_odm)
|
|
/* ODM combine could prevent us from supporting more planes
|
|
* we will reset ODM slice count back to 1 when all planes have
|
|
* been removed to maximize the amount of planes supported when
|
|
* new planes are added.
|
|
*/
|
|
resource_update_pipes_for_stream_with_slice_count(
|
|
state, dc->current_state, dc->res_pool, stream, 1);
|
|
|
|
otg_master_pipe = resource_get_otg_master_for_stream(
|
|
&state->res_ctx, stream);
|
|
if (otg_master_pipe)
|
|
added = resource_append_dpp_pipes_for_plane_composition(state,
|
|
dc->current_state, pool, otg_master_pipe, plane_state);
|
|
|
|
if (added) {
|
|
stream_status->plane_states[stream_status->plane_count] =
|
|
plane_state;
|
|
stream_status->plane_count++;
|
|
dc_plane_state_retain(plane_state);
|
|
}
|
|
|
|
out:
|
|
return added;
|
|
}
|
|
|
|
bool dc_state_remove_plane(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *stream,
|
|
struct dc_plane_state *plane_state,
|
|
struct dc_state *state)
|
|
{
|
|
int i;
|
|
struct dc_stream_status *stream_status = NULL;
|
|
struct resource_pool *pool = dc->res_pool;
|
|
|
|
if (!plane_state)
|
|
return true;
|
|
|
|
for (i = 0; i < state->stream_count; i++)
|
|
if (state->streams[i] == stream) {
|
|
stream_status = &state->stream_status[i];
|
|
break;
|
|
}
|
|
|
|
if (stream_status == NULL) {
|
|
dm_error("Existing stream not found; failed to remove plane.\n");
|
|
return false;
|
|
}
|
|
|
|
resource_remove_dpp_pipes_for_plane_composition(
|
|
state, pool, plane_state);
|
|
|
|
for (i = 0; i < stream_status->plane_count; i++) {
|
|
if (stream_status->plane_states[i] == plane_state) {
|
|
dc_plane_state_release(stream_status->plane_states[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == stream_status->plane_count) {
|
|
dm_error("Existing plane_state not found; failed to detach it!\n");
|
|
return false;
|
|
}
|
|
|
|
stream_status->plane_count--;
|
|
|
|
/* Start at the plane we've just released, and move all the planes one index forward to "trim" the array */
|
|
for (; i < stream_status->plane_count; i++)
|
|
stream_status->plane_states[i] = stream_status->plane_states[i + 1];
|
|
|
|
stream_status->plane_states[stream_status->plane_count] = NULL;
|
|
|
|
if (stream_status->plane_count == 0 && dc->config.enable_windowed_mpo_odm)
|
|
/* ODM combine could prevent us from supporting more planes
|
|
* we will reset ODM slice count back to 1 when all planes have
|
|
* been removed to maximize the amount of planes supported when
|
|
* new planes are added.
|
|
*/
|
|
resource_update_pipes_for_stream_with_slice_count(
|
|
state, dc->current_state, dc->res_pool, stream, 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* dc_state_rem_all_planes_for_stream - Remove planes attached to the target stream.
|
|
*
|
|
* @dc: Current dc state.
|
|
* @stream: Target stream, which we want to remove the attached plans.
|
|
* @state: context from which the planes are to be removed.
|
|
*
|
|
* Return:
|
|
* Return true if DC was able to remove all planes from the target
|
|
* stream, otherwise, return false.
|
|
*/
|
|
bool dc_state_rem_all_planes_for_stream(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *stream,
|
|
struct dc_state *state)
|
|
{
|
|
int i, old_plane_count;
|
|
struct dc_stream_status *stream_status = NULL;
|
|
struct dc_plane_state *del_planes[MAX_SURFACE_NUM] = { 0 };
|
|
|
|
for (i = 0; i < state->stream_count; i++)
|
|
if (state->streams[i] == stream) {
|
|
stream_status = &state->stream_status[i];
|
|
break;
|
|
}
|
|
|
|
if (stream_status == NULL) {
|
|
dm_error("Existing stream %p not found!\n", stream);
|
|
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 (!dc_state_remove_plane(dc, stream, del_planes[i], state))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dc_state_add_all_planes_for_stream(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *stream,
|
|
struct dc_plane_state * const *plane_states,
|
|
int plane_count,
|
|
struct dc_state *state)
|
|
{
|
|
int i;
|
|
bool result = true;
|
|
|
|
for (i = 0; i < plane_count; i++)
|
|
if (!dc_state_add_plane(dc, stream, plane_states[i], state)) {
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Private dc_state functions */
|
|
|
|
/**
|
|
* dc_state_get_stream_status - Get stream status from given dc state
|
|
* @state: DC state to find the stream status in
|
|
* @stream: The stream to get the stream status for
|
|
*
|
|
* The given stream is expected to exist in the given dc state. Otherwise, NULL
|
|
* will be returned.
|
|
*/
|
|
struct dc_stream_status *dc_state_get_stream_status(
|
|
struct dc_state *state,
|
|
struct dc_stream_state *stream)
|
|
{
|
|
uint8_t i;
|
|
|
|
if (state == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; i < state->stream_count; i++) {
|
|
if (stream == state->streams[i])
|
|
return &state->stream_status[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
enum mall_stream_type dc_state_get_pipe_subvp_type(const struct dc_state *state,
|
|
const struct pipe_ctx *pipe_ctx)
|
|
{
|
|
return dc_state_get_stream_subvp_type(state, pipe_ctx->stream);
|
|
}
|
|
|
|
enum mall_stream_type dc_state_get_stream_subvp_type(const struct dc_state *state,
|
|
const struct dc_stream_state *stream)
|
|
{
|
|
int i;
|
|
|
|
enum mall_stream_type type = SUBVP_NONE;
|
|
|
|
for (i = 0; i < state->stream_count; i++) {
|
|
if (state->streams[i] == stream) {
|
|
type = state->stream_status[i].mall_stream_config.type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
struct dc_stream_state *dc_state_get_paired_subvp_stream(const struct dc_state *state,
|
|
const struct dc_stream_state *stream)
|
|
{
|
|
int i;
|
|
|
|
struct dc_stream_state *paired_stream = NULL;
|
|
|
|
for (i = 0; i < state->stream_count; i++) {
|
|
if (state->streams[i] == stream) {
|
|
paired_stream = state->stream_status[i].mall_stream_config.paired_stream;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return paired_stream;
|
|
}
|
|
|
|
struct dc_stream_state *dc_state_create_phantom_stream(const struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *main_stream)
|
|
{
|
|
struct dc_stream_state *phantom_stream;
|
|
|
|
DC_LOGGER_INIT(dc->ctx->logger);
|
|
|
|
phantom_stream = dc_create_stream_for_sink(main_stream->sink);
|
|
|
|
if (!phantom_stream) {
|
|
DC_LOG_ERROR("Failed to allocate phantom stream.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* track phantom stream in dc_state */
|
|
dc_state_track_phantom_stream(state, phantom_stream);
|
|
|
|
phantom_stream->is_phantom = true;
|
|
phantom_stream->signal = SIGNAL_TYPE_VIRTUAL;
|
|
phantom_stream->dpms_off = true;
|
|
|
|
return phantom_stream;
|
|
}
|
|
|
|
void dc_state_release_phantom_stream(const struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *phantom_stream)
|
|
{
|
|
DC_LOGGER_INIT(dc->ctx->logger);
|
|
|
|
if (!dc_state_untrack_phantom_stream(state, phantom_stream)) {
|
|
DC_LOG_ERROR("Failed to free phantom stream %p in dc state %p.\n", phantom_stream, state);
|
|
return;
|
|
}
|
|
|
|
dc_stream_release(phantom_stream);
|
|
}
|
|
|
|
struct dc_plane_state *dc_state_create_phantom_plane(struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_plane_state *main_plane)
|
|
{
|
|
struct dc_plane_state *phantom_plane = dc_create_plane_state(dc);
|
|
|
|
DC_LOGGER_INIT(dc->ctx->logger);
|
|
|
|
if (!phantom_plane) {
|
|
DC_LOG_ERROR("Failed to allocate phantom plane.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* track phantom inside dc_state */
|
|
dc_state_track_phantom_plane(state, phantom_plane);
|
|
|
|
phantom_plane->is_phantom = true;
|
|
|
|
return phantom_plane;
|
|
}
|
|
|
|
void dc_state_release_phantom_plane(const struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_plane_state *phantom_plane)
|
|
{
|
|
DC_LOGGER_INIT(dc->ctx->logger);
|
|
|
|
if (!dc_state_untrack_phantom_plane(state, phantom_plane)) {
|
|
DC_LOG_ERROR("Failed to free phantom plane %p in dc state %p.\n", phantom_plane, state);
|
|
return;
|
|
}
|
|
|
|
dc_plane_state_release(phantom_plane);
|
|
}
|
|
|
|
/* add phantom streams to context and generate correct meta inside dc_state */
|
|
enum dc_status dc_state_add_phantom_stream(struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *phantom_stream,
|
|
struct dc_stream_state *main_stream)
|
|
{
|
|
struct dc_stream_status *main_stream_status;
|
|
struct dc_stream_status *phantom_stream_status;
|
|
enum dc_status res = dc_state_add_stream(dc, state, phantom_stream);
|
|
|
|
/* check if stream is tracked */
|
|
if (res == DC_OK && !dc_state_is_phantom_stream_tracked(state, phantom_stream)) {
|
|
/* stream must be tracked if added to state */
|
|
dc_state_track_phantom_stream(state, phantom_stream);
|
|
}
|
|
|
|
/* setup subvp meta */
|
|
main_stream_status = dc_state_get_stream_status(state, main_stream);
|
|
phantom_stream_status = dc_state_get_stream_status(state, phantom_stream);
|
|
phantom_stream_status->mall_stream_config.type = SUBVP_PHANTOM;
|
|
phantom_stream_status->mall_stream_config.paired_stream = main_stream;
|
|
main_stream_status->mall_stream_config.type = SUBVP_MAIN;
|
|
main_stream_status->mall_stream_config.paired_stream = phantom_stream;
|
|
|
|
return res;
|
|
}
|
|
|
|
enum dc_status dc_state_remove_phantom_stream(struct dc *dc,
|
|
struct dc_state *state,
|
|
struct dc_stream_state *phantom_stream)
|
|
{
|
|
struct dc_stream_status *main_stream_status;
|
|
struct dc_stream_status *phantom_stream_status;
|
|
|
|
/* reset subvp meta */
|
|
phantom_stream_status = dc_state_get_stream_status(state, phantom_stream);
|
|
main_stream_status = dc_state_get_stream_status(state, phantom_stream_status->mall_stream_config.paired_stream);
|
|
phantom_stream_status->mall_stream_config.type = SUBVP_NONE;
|
|
phantom_stream_status->mall_stream_config.paired_stream = NULL;
|
|
if (main_stream_status) {
|
|
main_stream_status->mall_stream_config.type = SUBVP_NONE;
|
|
main_stream_status->mall_stream_config.paired_stream = NULL;
|
|
}
|
|
|
|
/* remove stream from state */
|
|
return dc_state_remove_stream(dc, state, phantom_stream);
|
|
}
|
|
|
|
bool dc_state_add_phantom_plane(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *phantom_stream,
|
|
struct dc_plane_state *phantom_plane,
|
|
struct dc_state *state)
|
|
{
|
|
bool res = dc_state_add_plane(dc, phantom_stream, phantom_plane, state);
|
|
|
|
/* check if stream is tracked */
|
|
if (res && !dc_state_is_phantom_plane_tracked(state, phantom_plane)) {
|
|
/* stream must be tracked if added to state */
|
|
dc_state_track_phantom_plane(state, phantom_plane);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool dc_state_remove_phantom_plane(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *phantom_stream,
|
|
struct dc_plane_state *phantom_plane,
|
|
struct dc_state *state)
|
|
{
|
|
return dc_state_remove_plane(dc, phantom_stream, phantom_plane, state);
|
|
}
|
|
|
|
bool dc_state_rem_all_phantom_planes_for_stream(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *phantom_stream,
|
|
struct dc_state *state,
|
|
bool should_release_planes)
|
|
{
|
|
int i, old_plane_count;
|
|
struct dc_stream_status *stream_status = NULL;
|
|
struct dc_plane_state *del_planes[MAX_SURFACE_NUM] = { 0 };
|
|
|
|
for (i = 0; i < state->stream_count; i++)
|
|
if (state->streams[i] == phantom_stream) {
|
|
stream_status = &state->stream_status[i];
|
|
break;
|
|
}
|
|
|
|
if (stream_status == NULL) {
|
|
dm_error("Existing stream %p not found!\n", phantom_stream);
|
|
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 (!dc_state_remove_plane(dc, phantom_stream, del_planes[i], state))
|
|
return false;
|
|
if (should_release_planes)
|
|
dc_state_release_phantom_plane(dc, state, del_planes[i]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dc_state_add_all_phantom_planes_for_stream(
|
|
const struct dc *dc,
|
|
struct dc_stream_state *phantom_stream,
|
|
struct dc_plane_state * const *phantom_planes,
|
|
int plane_count,
|
|
struct dc_state *state)
|
|
{
|
|
return dc_state_add_all_planes_for_stream(dc, phantom_stream, phantom_planes, plane_count, state);
|
|
}
|
|
|
|
bool dc_state_remove_phantom_streams_and_planes(
|
|
struct dc *dc,
|
|
struct dc_state *state)
|
|
{
|
|
int i;
|
|
bool removed_phantom = false;
|
|
struct dc_stream_state *phantom_stream = NULL;
|
|
|
|
for (i = 0; i < dc->res_pool->pipe_count; i++) {
|
|
struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
|
|
|
|
if (pipe->plane_state && pipe->stream && dc_state_get_pipe_subvp_type(state, pipe) == SUBVP_PHANTOM) {
|
|
phantom_stream = pipe->stream;
|
|
|
|
dc_state_rem_all_phantom_planes_for_stream(dc, phantom_stream, state, false);
|
|
dc_state_remove_phantom_stream(dc, state, phantom_stream);
|
|
removed_phantom = true;
|
|
}
|
|
}
|
|
return removed_phantom;
|
|
}
|
|
|
|
void dc_state_release_phantom_streams_and_planes(
|
|
struct dc *dc,
|
|
struct dc_state *state)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < state->phantom_stream_count; i++)
|
|
dc_state_release_phantom_stream(dc, state, state->phantom_streams[i]);
|
|
|
|
for (i = 0; i < state->phantom_plane_count; i++)
|
|
dc_state_release_phantom_plane(dc, state, state->phantom_planes[i]);
|
|
}
|