drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c
Extension
.c
Size
47044 bytes
Lines
1250
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz == 0) {
			/* dpp == NULL && dppclk_khz == 0 is valid because of pipe harvesting.
			 * In this case just continue in loop
			 */
			continue;
		} else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz > 0) {
			/* The software state is not valid if dpp resource is NULL and
			 * dppclk_khz > 0.
			 */
			ASSERT(false);
			continue;
		}

		prev_dppclk_khz = clk_mgr->dccg->pipe_dppclk_khz[i];

		if (safe_to_lower || prev_dppclk_khz < dppclk_khz)
			clk_mgr->dccg->funcs->update_dpp_dto(
							clk_mgr->dccg, dpp_inst, dppclk_khz);
	}
}

static void dcn32_update_clocks_update_dentist(
		struct clk_mgr_internal *clk_mgr,
		struct dc_state *context)
{
	uint32_t new_disp_divider = 0;
	uint32_t new_dispclk_wdivider = 0;
	uint32_t old_dispclk_wdivider = 0;
	uint32_t i;
	uint32_t dentist_dispclk_wdivider_readback = 0;
	struct dc *dc = clk_mgr->base.ctx->dc;

	if (clk_mgr->base.clks.dispclk_khz == 0)
		return;

	new_disp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
			* clk_mgr->base.dentist_vco_freq_khz / clk_mgr->base.clks.dispclk_khz;

	new_dispclk_wdivider = dentist_get_did_from_divider(new_disp_divider);
	REG_GET(DENTIST_DISPCLK_CNTL,
			DENTIST_DISPCLK_WDIVIDER, &old_dispclk_wdivider);

	/* When changing divider to or from 127, some extra programming is required to prevent corruption */
	if (old_dispclk_wdivider == 127 && new_dispclk_wdivider != 127) {
		for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
			struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
			uint32_t fifo_level;
			struct dccg *dccg = clk_mgr->base.ctx->dc->res_pool->dccg;
			struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
			int32_t N;
			int32_t j;

			if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER))
				continue;
			/* Virtual encoders don't have this function */
			if (!stream_enc->funcs->get_fifo_cal_average_level)
				continue;
			fifo_level = stream_enc->funcs->get_fifo_cal_average_level(
					stream_enc);
			N = fifo_level / 4;
			dccg->funcs->set_fifo_errdet_ovr_en(
					dccg,
					true);
			for (j = 0; j < N - 4; j++)
				dccg->funcs->otg_drop_pixel(
						dccg,
						pipe_ctx->stream_res.tg->inst);
			dccg->funcs->set_fifo_errdet_ovr_en(
					dccg,
					false);
		}
	} else if (new_dispclk_wdivider == 127 && old_dispclk_wdivider != 127) {
		/* request clock with 126 divider first */
		uint32_t temp_disp_divider = dentist_get_divider_from_did(126);
		uint32_t temp_dispclk_khz = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR * clk_mgr->base.dentist_vco_freq_khz) / temp_disp_divider;

		if (clk_mgr->smu_present)
			/*
			 * SMU uses discrete dispclk presets. We applied
			 * the same formula to increase our dppclk_khz
			 * to the next matching discrete value. By
			 * contract, we should use the preset dispclk
			 * floored in Mhz to describe the intended clock.
			 */
			dcn32_smu_set_hard_min_by_freq(clk_mgr, PPCLK_DISPCLK,
					(uint16_t)khz_to_mhz_floor(temp_dispclk_khz));

		if (dc->debug.override_dispclk_programming) {
			REG_GET(DENTIST_DISPCLK_CNTL,
					DENTIST_DISPCLK_WDIVIDER, &dentist_dispclk_wdivider_readback);

Annotation

Implementation Notes