Commit 68f1a00c authored by Anthony Koo's avatar Anthony Koo Committed by Alex Deucher

drm/amd/display: interface to check if timing can be seamless

[Why]
Need to figure out whether a timing we want to commit matches
something that GOP already programmed, in which case
we can decide to some optimizations

[How]
1. Add way to check for DIG FE
2. Add way to check for matching OTG timing
3. Add way to check for matching pixel clock (if possible)
    - Currently only support DP for pixel clock, since it is easy to calc
Signed-off-by: default avatarAnthony Koo <Anthony.Koo@amd.com>
Reviewed-by: default avatarAric Cyr <Aric.Cyr@amd.com>
Acked-by: default avatarBhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c00800c4
......@@ -969,6 +969,52 @@ static bool context_changed(
return false;
}
bool dc_validate_seamless_boot_timing(struct dc *dc,
const struct dc_sink *sink,
struct dc_crtc_timing *crtc_timing)
{
struct timing_generator *tg;
struct dc_link *link = sink->link;
unsigned int inst;
/* Check for enabled DIG to identify enabled display */
if (!link->link_enc->funcs->is_dig_enabled(link->link_enc))
return false;
/* Check for which front end is used by this encoder.
* Note the inst is 1 indexed, where 0 is undefined.
* Note that DIG_FE can source from different OTG but our
* current implementation always map 1-to-1, so this code makes
* the same assumption and doesn't check OTG source.
*/
inst = link->link_enc->funcs->get_dig_frontend(link->link_enc) - 1;
/* Instance should be within the range of the pool */
if (inst >= dc->res_pool->pipe_count)
return false;
tg = dc->res_pool->timing_generators[inst];
if (!tg->funcs->is_matching_timing)
return false;
if (!tg->funcs->is_matching_timing(tg, crtc_timing))
return false;
if (dc_is_dp_signal(link->connector_signal)) {
unsigned int pix_clk_100hz;
dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
dc->res_pool->dp_clock_source,
inst, &pix_clk_100hz);
if (crtc_timing->pix_clk_100hz != pix_clk_100hz)
return false;
}
return true;
}
bool dc_enable_stereo(
struct dc *dc,
struct dc_state *context,
......
......@@ -594,6 +594,10 @@ struct dc_validation_set {
uint8_t plane_count;
};
bool dc_validate_seamless_boot_timing(struct dc *dc,
const struct dc_sink *sink,
struct dc_crtc_timing *crtc_timing);
enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state);
void get_clock_requirements_for_state(struct dc_state *state, struct AsicStateEx *info);
......
......@@ -977,6 +977,28 @@ static bool dce110_clock_source_power_down(
return bp_result == BP_RESULT_OK;
}
static bool get_pixel_clk_frequency_100hz(
struct clock_source *clock_source,
unsigned int inst,
unsigned int *pixel_clk_khz)
{
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source);
unsigned int clock_hz = 0;
if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) {
clock_hz = REG_READ(PHASE[inst]);
/* NOTE: There is agreement with VBIOS here that MODULO is
* programmed equal to DPREFCLK, in which case PHASE will be
* equivalent to pixel clock.
*/
*pixel_clk_khz = clock_hz / 100;
return true;
}
return false;
}
/*****************************************/
/* Constructor */
/*****************************************/
......@@ -984,12 +1006,14 @@ static bool dce110_clock_source_power_down(
static const struct clock_source_funcs dce112_clk_src_funcs = {
.cs_power_down = dce110_clock_source_power_down,
.program_pix_clk = dce112_program_pix_clk,
.get_pix_clk_dividers = dce112_get_pix_clk_dividers
.get_pix_clk_dividers = dce112_get_pix_clk_dividers,
.get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz
};
static const struct clock_source_funcs dce110_clk_src_funcs = {
.cs_power_down = dce110_clock_source_power_down,
.program_pix_clk = dce110_program_pix_clk,
.get_pix_clk_dividers = dce110_get_pix_clk_dividers
.get_pix_clk_dividers = dce110_get_pix_clk_dividers,
.get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz
};
......
......@@ -85,6 +85,7 @@ static const struct link_encoder_funcs dcn10_lnk_enc_funcs = {
.enable_hpd = dcn10_link_encoder_enable_hpd,
.disable_hpd = dcn10_link_encoder_disable_hpd,
.is_dig_enabled = dcn10_is_dig_enabled,
.get_dig_frontend = dcn10_get_dig_frontend,
.destroy = dcn10_link_encoder_destroy
};
......@@ -495,6 +496,15 @@ bool dcn10_is_dig_enabled(struct link_encoder *enc)
return value;
}
unsigned int dcn10_get_dig_frontend(struct link_encoder *enc)
{
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
uint32_t value;
REG_GET(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, &value);
return value;
}
static void link_encoder_disable(struct dcn10_link_encoder *enc10)
{
/* reset training pattern */
......
......@@ -336,6 +336,8 @@ void dcn10_psr_program_secondary_packet(struct link_encoder *enc,
bool dcn10_is_dig_enabled(struct link_encoder *enc);
unsigned int dcn10_get_dig_frontend(struct link_encoder *enc);
void dcn10_aux_initialize(struct dcn10_link_encoder *enc10);
#endif /* __DC_LINK_ENCODER__DCN10_H__ */
......@@ -1208,6 +1208,64 @@ bool optc1_is_stereo_left_eye(struct timing_generator *optc)
return ret;
}
bool optc1_is_matching_timing(struct timing_generator *tg,
const struct dc_crtc_timing *otg_timing)
{
struct dc_crtc_timing hw_crtc_timing = {0};
struct dcn_otg_state s = {0};
if (tg == NULL || otg_timing == NULL)
return false;
optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s);
hw_crtc_timing.h_total = s.h_total + 1;
hw_crtc_timing.h_addressable = s.h_total - ((s.h_total - s.h_blank_start) + s.h_blank_end);
hw_crtc_timing.h_front_porch = s.h_total + 1 - s.h_blank_start;
hw_crtc_timing.h_sync_width = s.h_sync_a_end - s.h_sync_a_start;
hw_crtc_timing.v_total = s.v_total + 1;
hw_crtc_timing.v_addressable = s.v_total - ((s.v_total - s.v_blank_start) + s.v_blank_end);
hw_crtc_timing.v_front_porch = s.v_total + 1 - s.v_blank_start;
hw_crtc_timing.v_sync_width = s.v_sync_a_end - s.v_sync_a_start;
if (otg_timing->h_total != hw_crtc_timing.h_total)
return false;
if (otg_timing->h_border_left != hw_crtc_timing.h_border_left)
return false;
if (otg_timing->h_addressable != hw_crtc_timing.h_addressable)
return false;
if (otg_timing->h_border_right != hw_crtc_timing.h_border_right)
return false;
if (otg_timing->h_front_porch != hw_crtc_timing.h_front_porch)
return false;
if (otg_timing->h_sync_width != hw_crtc_timing.h_sync_width)
return false;
if (otg_timing->v_total != hw_crtc_timing.v_total)
return false;
if (otg_timing->v_border_top != hw_crtc_timing.v_border_top)
return false;
if (otg_timing->v_addressable != hw_crtc_timing.v_addressable)
return false;
if (otg_timing->v_border_bottom != hw_crtc_timing.v_border_bottom)
return false;
if (otg_timing->v_sync_width != hw_crtc_timing.v_sync_width)
return false;
return true;
}
void optc1_read_otg_state(struct optc *optc1,
struct dcn_otg_state *s)
{
......@@ -1404,6 +1462,7 @@ static const struct timing_generator_funcs dcn10_tg_funcs = {
.get_frame_count = optc1_get_vblank_counter,
.get_scanoutpos = optc1_get_crtc_scanoutpos,
.get_otg_active_size = optc1_get_otg_active_size,
.is_matching_timing = optc1_is_matching_timing,
.set_early_control = optc1_set_early_control,
/* used by enable_timing_synchronization. Not need for FPGA */
.wait_for_state = optc1_wait_for_state,
......
......@@ -166,6 +166,10 @@ struct clock_source_funcs {
struct clock_source *,
struct pixel_clk_params *,
struct pll_settings *);
bool (*get_pixel_clk_frequency_100hz)(
struct clock_source *clock_source,
unsigned int inst,
unsigned int *pixel_clk_khz);
};
struct clock_source {
......
......@@ -153,6 +153,7 @@ struct link_encoder_funcs {
void (*enable_hpd)(struct link_encoder *enc);
void (*disable_hpd)(struct link_encoder *enc);
bool (*is_dig_enabled)(struct link_encoder *enc);
unsigned int (*get_dig_frontend)(struct link_encoder *enc);
void (*destroy)(struct link_encoder **enc);
};
......
......@@ -170,6 +170,8 @@ struct timing_generator_funcs {
bool (*get_otg_active_size)(struct timing_generator *optc,
uint32_t *otg_active_width,
uint32_t *otg_active_height);
bool (*is_matching_timing)(struct timing_generator *tg,
const struct dc_crtc_timing *otg_timing);
void (*set_early_control)(struct timing_generator *tg,
uint32_t early_cntl);
void (*wait_for_state)(struct timing_generator *tg,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment