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