From: Ovidiu Bunea Date: Fri, 1 May 2026 23:38:13 +0000 (-0400) Subject: drm/amd/display: Update get_pixel_clk_frequency() for DCN4x DCCG DP DTO X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6f6483dbfacd2269da68cb97db996c0f31f650f2;p=thirdparty%2Flinux.git drm/amd/display: Update get_pixel_clk_frequency() for DCN4x DCCG DP DTO [Why & How] DCN4x ASICs have different DCCG logic for programming DP DTO. The current get_pixel_clk_frequency_100hz() function does not account for this. Rename the function to "get_dp_dto_frequency" to more accurately reflect its intended behaviour. Create a new function that correctly calculates the target pixel rate for DCN4.x DCCG design and use it. Reviewed-by: Leo Chen Signed-off-by: Ovidiu Bunea Signed-off-by: Ray Wu Signed-off-by: Alex Deucher --- diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 6d520d5b02e9..06864b8683f0 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1487,7 +1487,7 @@ static void disable_vbios_mode_if_required( } } - dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( + dc->res_pool->dp_clock_source->funcs->get_dp_dto_frequency_100hz( dc->res_pool->dp_clock_source, tg_inst, &pix_clk_100hz); @@ -2004,7 +2004,7 @@ bool dc_validate_boot_timing(const struct dc *dc, uint32_t numOdmPipes = 1; uint32_t id_src[4] = {0}; - dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( + dc->res_pool->dp_clock_source->funcs->get_dp_dto_frequency_100hz( dc->res_pool->dp_clock_source, tg_inst, &pix_clk_100hz); diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index ac9eed58c415..09910333bd71 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1188,15 +1188,16 @@ static bool dce110_clock_source_power_down( return bp_result == BP_RESULT_OK; } -static bool get_pixel_clk_frequency_100hz( +static bool get_dp_dto_frequency_100hz( const struct clock_source *clock_source, unsigned int inst, - unsigned int *pixel_clk_khz) + unsigned int *pixel_clk_100hz) { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); unsigned int clock_hz = 0; unsigned int modulo_hz = 0; unsigned int dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz; + unsigned long long temp = 0; if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) { clock_hz = REG_READ(PHASE[inst]); @@ -1207,17 +1208,18 @@ static bool get_pixel_clk_frequency_100hz( * not be programmed equal to DPREFCLK */ modulo_hz = REG_READ(MODULO[inst]); - if (modulo_hz) - *pixel_clk_khz = (unsigned int)div_u64((uint64_t)clock_hz * - dp_dto_ref_khz * 10, modulo_hz); - else - *pixel_clk_khz = 0; + if (modulo_hz) { + temp = div_u64((uint64_t)clock_hz * dp_dto_ref_khz * 10, modulo_hz); + ASSERT(temp / 100 <= 0xFFFFFFFFUL); + *pixel_clk_100hz = (unsigned int)(temp / 100); + } else + *pixel_clk_100hz = 0; } else { /* 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; + *pixel_clk_100hz = clock_hz / 100; } return true; } @@ -1225,6 +1227,61 @@ static bool get_pixel_clk_frequency_100hz( return false; } +static bool dcn401_get_dp_dto_frequency_100hz(const struct clock_source *clock_source, unsigned int inst, + unsigned int *pixel_clk_100hz) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + unsigned int phase_hz = 0; + unsigned int modulo_hz = 0; + unsigned int dp_dto_integer = 0; + unsigned long long temp = 0; + + if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) { + phase_hz = REG_READ(PHASE[inst]); + modulo_hz = REG_READ(MODULO[inst]); + + switch (inst) { + case 0: + REG_GET(OTG_PIXEL_RATE_DIV, DPDTO0_INT, &dp_dto_integer); + break; + case 1: + REG_GET(OTG_PIXEL_RATE_DIV, DPDTO1_INT, &dp_dto_integer); + break; + case 2: + REG_GET(OTG_PIXEL_RATE_DIV, DPDTO2_INT, &dp_dto_integer); + break; + case 3: + REG_GET(OTG_PIXEL_RATE_DIV, DPDTO3_INT, &dp_dto_integer); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + /* On DCN4x, the DCCG DPDTO is directly programmed with the required pixel clock as per the following formula: + * - DPDTO INTEGER = INT(4:4:4 pixel rate / DTBCLK_P rate) + * - DPDTO PHASE = 4:4:4 pixel rate – DPDTO INTEGER * DTBCLK_P rate + * - DPDTO MODULO = DTBCLK_P rate + * - target pix_clk_hz = (DPDTO INTEGER * DPDTO MODULO + DPDTO PHASE) + */ + + dp_dto_integer += 1; // integer=0 represents 1x multiplier, etc. + temp = (unsigned long long)dp_dto_integer * modulo_hz + phase_hz; + + if (temp / 100 > 0xFFFFFFFFUL) { + /* pixel rate 100hz should never be this high, if it is, throw an assert and return 0 */ + BREAK_TO_DEBUGGER(); + *pixel_clk_100hz = 0; + } else { + *pixel_clk_100hz = (unsigned int)(temp / 100); + } + + return true; + } + + return false; +} + /* this table is use to find *1.001 and /1.001 pixel rates from non-precise pixel rate */ const struct pixel_rate_range_table_entry video_optimized_pixel_rates[] = { // /1.001 rates @@ -1318,7 +1375,7 @@ static const struct clock_source_funcs dcn20_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn20_program_pix_clk, .get_pix_clk_dividers = dce112_get_pix_clk_dividers, - .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz, + .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz, .override_dp_pix_clk = dcn20_override_dp_pix_clk }; @@ -1405,21 +1462,21 @@ static const struct clock_source_funcs dcn3_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn3_program_pix_clk, .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, - .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz + .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; static const struct clock_source_funcs dcn31_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn31_program_pix_clk, .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, - .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz + .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; static const struct clock_source_funcs dcn401_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn401_program_pix_clk, .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, - .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz + .get_dp_dto_frequency_100hz = dcn401_get_dp_dto_frequency_100hz }; /*****************************************/ @@ -1430,13 +1487,13 @@ 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_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz + .get_dp_dto_frequency_100hz = get_dp_dto_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_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz + .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h index 94128f7a18b1..9441aa6a897f 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h @@ -202,7 +202,11 @@ type PLL_REF_DIV; \ type DP_DTO0_PHASE; \ type DP_DTO0_MODULO; \ - type DP_DTO0_ENABLE; + type DP_DTO0_ENABLE; \ + type DPDTO0_INT; \ + type DPDTO1_INT; \ + type DPDTO2_INT; \ + type DPDTO3_INT; #define CS_REG_FIELD_LIST_DCN32(type) \ type PIPE0_DTO_SRC_SEL; @@ -221,6 +225,7 @@ struct dce110_clk_src_regs { uint32_t RESYNC_CNTL; uint32_t PIXCLK_RESYNC_CNTL; uint32_t PLL_CNTL; + uint32_t OTG_PIXEL_RATE_DIV; /* below are for DTO. * todo: should probably use different struct to not waste space diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 1733f551248e..7f58930fc87e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -2423,7 +2423,7 @@ static int dcn10_align_pixel_clocks(struct dc *dc, int group_size, grouped_pipes[i]->stream_res.tg->funcs->get_hw_timing( grouped_pipes[i]->stream_res.tg, &hw_crtc_timing[i]); - dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( + dc->res_pool->dp_clock_source->funcs->get_dp_dto_frequency_100hz( dc->res_pool->dp_clock_source, grouped_pipes[i]->stream_res.tg->inst, &pclk); @@ -2462,7 +2462,7 @@ static int dcn10_align_pixel_clocks(struct dc *dc, int group_size, dc->res_pool->dp_clock_source, grouped_pipes[i]->stream_res.tg->inst, (unsigned int)phase[i], (unsigned int)modulo[i]); - dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( + dc->res_pool->dp_clock_source->funcs->get_dp_dto_frequency_100hz( dc->res_pool->dp_clock_source, grouped_pipes[i]->stream_res.tg->inst, &pclk); grouped_pipes[i]->stream->timing.pix_clk_100hz = diff --git a/drivers/gpu/drm/amd/display/dc/inc/clock_source.h b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h index ed2f8005d85e..6bbbb8ea7dad 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/clock_source.h +++ b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h @@ -170,10 +170,10 @@ struct clock_source_funcs { struct clock_source *, struct pixel_clk_params *, struct pll_settings *); - bool (*get_pixel_clk_frequency_100hz)( + bool (*get_dp_dto_frequency_100hz)( const struct clock_source *clock_source, unsigned int inst, - unsigned int *pixel_clk_khz); + unsigned int *pixel_clk_100hz); bool (*override_dp_pix_clk)( struct clock_source *clock_source, unsigned int inst,