brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · 4e93eee Raw
487 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright 2022 Advanced Micro Devices, Inc.4 *5 * Permission is hereby granted, free of charge, to any person obtaining a6 * copy of this software and associated documentation files (the "Software"),7 * to deal in the Software without restriction, including without limitation8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,9 * and/or sell copies of the Software, and to permit persons to whom the10 * Software is furnished to do so, subject to the following conditions:11 *12 * The above copyright notice and this permission notice shall be included in13 * all copies or substantial portions of the Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR21 * OTHER DEALINGS IN THE SOFTWARE.22 *23 * Authors: AMD24 *25 */26 27 28#include "dm_services.h"29#include "dm_helpers.h"30#include "core_types.h"31#include "resource.h"32#include "dccg.h"33#include "dce/dce_hwseq.h"34#include "clk_mgr.h"35#include "reg_helper.h"36#include "abm.h"37#include "hubp.h"38#include "dchubbub.h"39#include "timing_generator.h"40#include "opp.h"41#include "ipp.h"42#include "mpc.h"43#include "mcif_wb.h"44#include "dc_dmub_srv.h"45#include "dcn314_hwseq.h"46#include "link_hwss.h"47#include "dpcd_defs.h"48#include "dce/dmub_outbox.h"49#include "link.h"50#include "dcn10/dcn10_hwseq.h"51#include "inc/link_enc_cfg.h"52#include "dcn30/dcn30_vpg.h"53#include "dce/dce_i2c_hw.h"54#include "dsc.h"55#include "dcn20/dcn20_optc.h"56#include "dcn30/dcn30_cm_common.h"57 58#define DC_LOGGER_INIT(logger)59 60#define CTX \61	hws->ctx62#define REG(reg)\63	hws->regs->reg64#define DC_LOGGER \65	stream->ctx->logger66 67 68#undef FN69#define FN(reg_name, field_name) \70	hws->shifts->field_name, hws->masks->field_name71 72static void update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)73{74	struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc;75	struct dc_stream_state *stream = pipe_ctx->stream;76	struct pipe_ctx *odm_pipe;77	int opp_cnt = 1;78 79	ASSERT(dsc);80	for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)81		opp_cnt++;82 83	if (enable) {84		struct dsc_config dsc_cfg;85		struct dsc_optc_config dsc_optc_cfg = {0};86		enum optc_dsc_mode optc_dsc_mode;87 88		/* Enable DSC hw block */89		dsc_cfg.pic_width = (stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt;90		dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom;91		dsc_cfg.pixel_encoding = stream->timing.pixel_encoding;92		dsc_cfg.color_depth = stream->timing.display_color_depth;93		dsc_cfg.is_odm = pipe_ctx->next_odm_pipe ? true : false;94		dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;95		ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);96		dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;97 98		dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg);99		dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);100		for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {101			struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc;102 103			ASSERT(odm_dsc);104			odm_dsc->funcs->dsc_set_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg);105			odm_dsc->funcs->dsc_enable(odm_dsc, odm_pipe->stream_res.opp->inst);106		}107		dsc_cfg.dc_dsc_cfg.num_slices_h *= opp_cnt;108		dsc_cfg.pic_width *= opp_cnt;109 110		optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;111 112		/* Enable DSC in OPTC */113		DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst);114		pipe_ctx->stream_res.tg->funcs->set_dsc_config(pipe_ctx->stream_res.tg,115							optc_dsc_mode,116							dsc_optc_cfg.bytes_per_pixel,117							dsc_optc_cfg.slice_width);118	} else {119		/* disable DSC in OPTC */120		pipe_ctx->stream_res.tg->funcs->set_dsc_config(121				pipe_ctx->stream_res.tg,122				OPTC_DSC_DISABLED, 0, 0);123 124		/* disable DSC block */125		dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc);126		for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {127			ASSERT(odm_pipe->stream_res.dsc);128			odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc);129		}130	}131}132 133// Given any pipe_ctx, return the total ODM combine factor, and optionally return134// the OPPids which are used135static unsigned int get_odm_config(struct pipe_ctx *pipe_ctx, unsigned int *opp_instances)136{137	unsigned int opp_count = 1;138	struct pipe_ctx *odm_pipe;139 140	// First get to the top pipe141	for (odm_pipe = pipe_ctx; odm_pipe->prev_odm_pipe; odm_pipe = odm_pipe->prev_odm_pipe)142		;143 144	// First pipe is always used145	if (opp_instances)146		opp_instances[0] = odm_pipe->stream_res.opp->inst;147 148	// Find and count odm pipes, if any149	for (odm_pipe = odm_pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {150		if (opp_instances)151			opp_instances[opp_count] = odm_pipe->stream_res.opp->inst;152		opp_count++;153	}154 155	return opp_count;156}157 158void dcn314_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx)159{160	struct pipe_ctx *odm_pipe;161	int opp_cnt = 0;162	int opp_inst[MAX_PIPES] = {0};163	int odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, false);164	int last_odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, true);165 166	opp_cnt = get_odm_config(pipe_ctx, opp_inst);167 168	if (opp_cnt > 1)169		pipe_ctx->stream_res.tg->funcs->set_odm_combine(170				pipe_ctx->stream_res.tg,171				opp_inst, opp_cnt,172				odm_slice_width, last_odm_slice_width);173	else174		pipe_ctx->stream_res.tg->funcs->set_odm_bypass(175				pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);176 177	for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {178		odm_pipe->stream_res.opp->funcs->opp_pipe_clock_control(179				odm_pipe->stream_res.opp,180				true);181	}182 183	if (pipe_ctx->stream_res.dsc) {184		struct pipe_ctx *current_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx];185 186		update_dsc_on_stream(pipe_ctx, pipe_ctx->stream->timing.flags.DSC);187 188		/* Check if no longer using pipe for ODM, then need to disconnect DSC for that pipe */189		if (!pipe_ctx->next_odm_pipe && current_pipe_ctx->next_odm_pipe &&190				current_pipe_ctx->next_odm_pipe->stream_res.dsc) {191			struct display_stream_compressor *dsc = current_pipe_ctx->next_odm_pipe->stream_res.dsc;192			/* disconnect DSC block from stream */193			dsc->funcs->dsc_disconnect(dsc);194		}195	}196}197 198void dcn314_dsc_pg_control(199		struct dce_hwseq *hws,200		unsigned int dsc_inst,201		bool power_on)202{203	uint32_t power_gate = power_on ? 0 : 1;204	uint32_t pwr_status = power_on ? 0 : 2;205	uint32_t org_ip_request_cntl = 0;206 207	if (hws->ctx->dc->debug.disable_dsc_power_gate)208		return;209 210	if (hws->ctx->dc->debug.root_clock_optimization.bits.dsc &&211		hws->ctx->dc->res_pool->dccg->funcs->enable_dsc &&212		power_on)213		hws->ctx->dc->res_pool->dccg->funcs->enable_dsc(214			hws->ctx->dc->res_pool->dccg, dsc_inst);215 216	REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);217	if (org_ip_request_cntl == 0)218		REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);219 220	switch (dsc_inst) {221	case 0: /* DSC0 */222		REG_UPDATE(DOMAIN16_PG_CONFIG,223				DOMAIN_POWER_GATE, power_gate);224 225		REG_WAIT(DOMAIN16_PG_STATUS,226				DOMAIN_PGFSM_PWR_STATUS, pwr_status,227				1, 1000);228		break;229	case 1: /* DSC1 */230		REG_UPDATE(DOMAIN17_PG_CONFIG,231				DOMAIN_POWER_GATE, power_gate);232 233		REG_WAIT(DOMAIN17_PG_STATUS,234				DOMAIN_PGFSM_PWR_STATUS, pwr_status,235				1, 1000);236		break;237	case 2: /* DSC2 */238		REG_UPDATE(DOMAIN18_PG_CONFIG,239				DOMAIN_POWER_GATE, power_gate);240 241		REG_WAIT(DOMAIN18_PG_STATUS,242				DOMAIN_PGFSM_PWR_STATUS, pwr_status,243				1, 1000);244		break;245	case 3: /* DSC3 */246		REG_UPDATE(DOMAIN19_PG_CONFIG,247				DOMAIN_POWER_GATE, power_gate);248 249		REG_WAIT(DOMAIN19_PG_STATUS,250				DOMAIN_PGFSM_PWR_STATUS, pwr_status,251				1, 1000);252		break;253	default:254		BREAK_TO_DEBUGGER();255		break;256	}257 258	if (org_ip_request_cntl == 0)259		REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0);260 261	if (hws->ctx->dc->debug.root_clock_optimization.bits.dsc) {262		if (hws->ctx->dc->res_pool->dccg->funcs->disable_dsc && !power_on)263			hws->ctx->dc->res_pool->dccg->funcs->disable_dsc(264				hws->ctx->dc->res_pool->dccg, dsc_inst);265	}266 267}268 269void dcn314_enable_power_gating_plane(struct dce_hwseq *hws, bool enable)270{271	bool force_on = true; /* disable power gating */272	uint32_t org_ip_request_cntl = 0;273 274	if (enable && !hws->ctx->dc->debug.disable_hubp_power_gate)275		force_on = false;276 277	REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);278	if (org_ip_request_cntl == 0)279		REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);280	/* DCHUBP0/1/2/3/4/5 */281	REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);282	REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);283	/* DPP0/1/2/3/4/5 */284	REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);285	REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);286 287	force_on = true; /* disable power gating */288	if (enable && !hws->ctx->dc->debug.disable_dsc_power_gate)289		force_on = false;290 291	/* DCS0/1/2/3/4 */292	REG_UPDATE(DOMAIN16_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);293	REG_UPDATE(DOMAIN17_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);294	REG_UPDATE(DOMAIN18_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);295	REG_UPDATE(DOMAIN19_PG_CONFIG, DOMAIN_POWER_FORCEON, force_on);296 297	if (org_ip_request_cntl == 0)298		REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0);299}300 301unsigned int dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div)302{303	struct dc_stream_state *stream = pipe_ctx->stream;304	unsigned int odm_combine_factor = 0;305	bool two_pix_per_container = false;306 307	two_pix_per_container = pipe_ctx->stream_res.tg->funcs->is_two_pixels_per_container(&stream->timing);308	odm_combine_factor = get_odm_config(pipe_ctx, NULL);309 310	if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) {311		*k1_div = PIXEL_RATE_DIV_BY_1;312		*k2_div = PIXEL_RATE_DIV_BY_1;313	} else if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal) || dc_is_dvi_signal(pipe_ctx->stream->signal)) {314		*k1_div = PIXEL_RATE_DIV_BY_1;315		if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)316			*k2_div = PIXEL_RATE_DIV_BY_2;317		else318			*k2_div = PIXEL_RATE_DIV_BY_4;319	} else if (dc_is_dp_signal(pipe_ctx->stream->signal) || dc_is_virtual_signal(pipe_ctx->stream->signal)) {320		if (two_pix_per_container) {321			*k1_div = PIXEL_RATE_DIV_BY_1;322			*k2_div = PIXEL_RATE_DIV_BY_2;323		} else {324			*k1_div = PIXEL_RATE_DIV_BY_1;325			*k2_div = PIXEL_RATE_DIV_BY_4;326			if (odm_combine_factor == 2)327				*k2_div = PIXEL_RATE_DIV_BY_2;328		}329	}330 331	if ((*k1_div == PIXEL_RATE_DIV_NA) && (*k2_div == PIXEL_RATE_DIV_NA))332		ASSERT(false);333 334	return odm_combine_factor;335}336 337void dcn314_calculate_pix_rate_divider(338		struct dc *dc,339		struct dc_state *context,340		const struct dc_stream_state *stream)341{342	struct dce_hwseq *hws = dc->hwseq;343	struct pipe_ctx *pipe_ctx = NULL;344	unsigned int k1_div = PIXEL_RATE_DIV_NA;345	unsigned int k2_div = PIXEL_RATE_DIV_NA;346 347	pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream);348 349	if (pipe_ctx) {350		if (hws->funcs.calculate_dccg_k1_k2_values)351			hws->funcs.calculate_dccg_k1_k2_values(pipe_ctx, &k1_div, &k2_div);352 353		pipe_ctx->pixel_rate_divider.div_factor1 = k1_div;354		pipe_ctx->pixel_rate_divider.div_factor2 = k2_div;355	}356}357 358void dcn314_resync_fifo_dccg_dio(struct dce_hwseq *hws, struct dc *dc, struct dc_state *context, unsigned int current_pipe_idx)359{360	unsigned int i;361	struct pipe_ctx *pipe = NULL;362	bool otg_disabled[MAX_PIPES] = {false};363 364	for (i = 0; i < dc->res_pool->pipe_count; i++) {365		if (i <= current_pipe_idx) {366			pipe = &context->res_ctx.pipe_ctx[i];367		} else {368			pipe = &dc->current_state->res_ctx.pipe_ctx[i];369		}370 371		if (pipe->top_pipe || pipe->prev_odm_pipe)372			continue;373 374		if (pipe->stream && (pipe->stream->dpms_off || dc_is_virtual_signal(pipe->stream->signal))) {375			pipe->stream_res.tg->funcs->disable_crtc(pipe->stream_res.tg);376			reset_sync_context_for_pipe(dc, context, i);377			otg_disabled[i] = true;378		}379	}380 381	hws->ctx->dc->res_pool->dccg->funcs->trigger_dio_fifo_resync(hws->ctx->dc->res_pool->dccg);382 383	for (i = 0; i < dc->res_pool->pipe_count; i++) {384		if (i <= current_pipe_idx)385			pipe = &context->res_ctx.pipe_ctx[i];386		else387			pipe = &dc->current_state->res_ctx.pipe_ctx[i];388 389		if (otg_disabled[i]) {390			int opp_inst[MAX_PIPES] = { pipe->stream_res.opp->inst };391			int opp_cnt = 1;392			int last_odm_slice_width = resource_get_odm_slice_dst_width(pipe, true);393			int odm_slice_width = resource_get_odm_slice_dst_width(pipe, false);394			struct pipe_ctx *odm_pipe;395 396			for (odm_pipe = pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {397				opp_inst[opp_cnt] = odm_pipe->stream_res.opp->inst;398				opp_cnt++;399			}400			if (opp_cnt > 1)401				pipe->stream_res.tg->funcs->set_odm_combine(402						pipe->stream_res.tg,403						opp_inst, opp_cnt,404						odm_slice_width,405						last_odm_slice_width);406			pipe->stream_res.tg->funcs->enable_crtc(pipe->stream_res.tg);407		}408	}409}410 411void dcn314_dpp_root_clock_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool clock_on)412{413	if (!hws->ctx->dc->debug.root_clock_optimization.bits.dpp)414		return;415 416	if (hws->ctx->dc->res_pool->dccg->funcs->dpp_root_clock_control)417		hws->ctx->dc->res_pool->dccg->funcs->dpp_root_clock_control(418			hws->ctx->dc->res_pool->dccg, dpp_inst, clock_on);419}420 421static void apply_symclk_on_tx_off_wa(struct dc_link *link)422{423	/* There are use cases where SYMCLK is referenced by OTG. For instance424	 * for TMDS signal, OTG relies SYMCLK even if TX video output is off.425	 * However current link interface will power off PHY when disabling link426	 * output. This will turn off SYMCLK generated by PHY. The workaround is427	 * to identify such case where SYMCLK is still in use by OTG when we428	 * power off PHY. When this is detected, we will temporarily power PHY429	 * back on and move PHY's SYMCLK state to SYMCLK_ON_TX_OFF by calling430	 * program_pix_clk interface. When OTG is disabled, we will then power431	 * off PHY by calling disable link output again.432	 *433	 * In future dcn generations, we plan to rework transmitter control434	 * interface so that we could have an option to set SYMCLK ON TX OFF435	 * state in one step without this workaround436	 */437 438	struct dc *dc = link->ctx->dc;439	struct pipe_ctx *pipe_ctx = NULL;440	uint8_t i;441 442	if (link->phy_state.symclk_ref_cnts.otg > 0) {443		for (i = 0; i < MAX_PIPES; i++) {444			pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];445			if (pipe_ctx->stream && pipe_ctx->stream->link == link && pipe_ctx->top_pipe == NULL) {446				pipe_ctx->clock_source->funcs->program_pix_clk(447						pipe_ctx->clock_source,448						&pipe_ctx->stream_res.pix_clk_params,449						dc->link_srv->dp_get_encoding_format(450								&pipe_ctx->link_config.dp_link_settings),451						&pipe_ctx->pll_settings);452				link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;453				break;454			}455		}456	}457}458 459void dcn314_disable_link_output(struct dc_link *link,460		const struct link_resource *link_res,461		enum signal_type signal)462{463	struct dc *dc = link->ctx->dc;464	const struct link_hwss *link_hwss = get_link_hwss(link, link_res);465	struct dmcu *dmcu = dc->res_pool->dmcu;466 467	if (signal == SIGNAL_TYPE_EDP &&468			link->dc->hwss.edp_backlight_control &&469			!link->skip_implict_edp_power_control)470		link->dc->hwss.edp_backlight_control(link, false);471	else if (dmcu != NULL && dmcu->funcs->lock_phy)472		dmcu->funcs->lock_phy(dmcu);473 474	link_hwss->disable_link_output(link, link_res, signal);475	link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;476	/*477	 * Add the logic to extract BOTH power up and power down sequences478	 * from enable/disable link output and only call edp panel control479	 * in enable_link_dp and disable_link_dp once.480	 */481	if (dmcu != NULL && dmcu->funcs->lock_phy)482		dmcu->funcs->unlock_phy(dmcu);483	dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY);484 485	apply_symclk_on_tx_off_wa(link);486}487