/* SPDX-License-Identifier: MIT */ /* * Copyright 2023 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. *
*/
enum symclk_fe_source {
SYMCLK_FE_SYMCLK_A = 0, // Select functional clock from backend symclk A
SYMCLK_FE_SYMCLK_B,
SYMCLK_FE_SYMCLK_C,
SYMCLK_FE_SYMCLK_D,
SYMCLK_FE_SYMCLK_E,
SYMCLK_FE_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum symclk_be_source {
SYMCLK_BE_PHYCLK = 0, // Select phy clk when sym_clk_enable = 1
SYMCLK_BE_DPIACLK_810 = 4,
SYMCLK_BE_DPIACLK_162 = 5,
SYMCLK_BE_DPIACLK_540 = 6,
SYMCLK_BE_DPIACLK_270 = 7,
SYMCLK_BE_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum physymclk_source {
PHYSYMCLK_PHYCLK = 0, // Select symclk as source of clock which is output to PHY through DCIO.
PHYSYMCLK_PHYD18CLK, // Select phyd18clk as the source of clock which is output to PHY through DCIO.
PHYSYMCLK_PHYD32CLK, // Select phyd32clk as the source of clock which is output to PHY through DCIO.
PHYSYMCLK_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum dtbclk_source {
DTBCLK_DPREFCLK = 0, // Selects source for DTBCLK_P# as DPREFCLK (src sel 0 and 1 are same)
DTBCLK_DPREFCLK_0, // Selects source for DTBCLK_P# as DPREFCLK (src sel 0 and 1 are same)
DTBCLK_DTBCLK0, // Selects source for DTBCLK_P# as DTBCLK0
DTBCLK_DTBCLK1, // Selects source for DTBCLK_P# as DTBCLK0
DTBCLK_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum dppclk_clock_source {
DPP_REFCLK = 0, // refclk is selected
DPP_DCCG_DTO, // Functional clock selected is DTO tuned DPPCLK
};
enum dp_stream_clk_source {
DP_STREAM_DTBCLK_P0 = 0, // Selects functional for DP_STREAM_CLK as DTBCLK_P#
DP_STREAM_DTBCLK_P1,
DP_STREAM_DTBCLK_P2,
DP_STREAM_DTBCLK_P3,
DP_STREAM_DTBCLK_P4,
DP_STREAM_DTBCLK_P5,
DP_STREAM_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum hdmi_char_clk {
HDMI_CHAR_PHYAD18CLK = 0, // Selects functional for hdmi_char_clk as UNIPHYA PHYD18CLK
HDMI_CHAR_PHYBD18CLK,
HDMI_CHAR_PHYCD18CLK,
HDMI_CHAR_PHYDD18CLK,
HDMI_CHAR_PHYED18CLK,
HDMI_CHAR_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum hdmi_stream_clk_source {
HDMI_STREAM_DTBCLK_P0 = 0, // Selects functional for HDMI_STREAM_CLK as DTBCLK_P#
HDMI_STREAM_DTBCLK_P1,
HDMI_STREAM_DTBCLK_P2,
HDMI_STREAM_DTBCLK_P3,
HDMI_STREAM_DTBCLK_P4,
HDMI_STREAM_DTBCLK_P5,
HDMI_STREAM_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum symclk32_se_clk_source {
SYMCLK32_SE_PHYAD32CLK = 0, // Selects functional for SYMCLK32 as UNIPHYA PHYD32CLK
SYMCLK32_SE_PHYBD32CLK,
SYMCLK32_SE_PHYCD32CLK,
SYMCLK32_SE_PHYDD32CLK,
SYMCLK32_SE_PHYED32CLK,
SYMCLK32_SE_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
enum symclk32_le_clk_source {
SYMCLK32_LE_PHYAD32CLK = 0, // Selects functional for SYMCLK32 as UNIPHYA PHYD32CLK
SYMCLK32_LE_PHYBD32CLK,
SYMCLK32_LE_PHYCD32CLK,
SYMCLK32_LE_PHYDD32CLK,
SYMCLK32_LE_PHYED32CLK,
SYMCLK32_LE_REFCLK = 0xFF, // Arbitrary value to pass refclk selection in software
};
staticvoid dccg35_disable_symclk_be_new( struct dccg *dccg, int inst)
{ int i;
/* Switch from functional clock to refclock */
dccg35_set_symclk_be_src_new(dccg, inst, SYMCLK_BE_REFCLK);
/* Check if any other SE connected LE and disable them */ for (i = 0; i < 4; i++) { /* Make sure FE is not already in RCG */ if (dccg35_is_fe_rcg(dccg, i) == 0) { if (dccg35_is_symclk_fe_src_functional_be(dccg, i, inst))
dccg35_disable_symclk_fe_new(dccg, i);
}
} /* Safe to RCG SYMCLK*/
dccg35_set_symclk_be_rcg(dccg, inst, true);
}
staticvoid dccg35_disable_symclk32_le_new( struct dccg *dccg, int inst)
{ int i;
/* Switch from functional clock to refclock */
dccg35_set_symclk32_le_src_new(dccg, inst, SYMCLK32_LE_REFCLK);
/* Check if any SE are connected and disable SE as well */ for (i = 0; i < 4; i++) { /* Make sure FE is not already in RCG */ if (dccg35_is_symclk32_se_rcg(dccg, i) == 0) { /* Disable and SE connected to this LE before RCG */ if (dccg35_is_symclk32_se_src_functional_le_new(dccg, i, inst))
dccg35_disable_symclk32_se_new(dccg, i);
}
} /* Safe to RCG SYM32_LE*/
dccg35_set_symclk32_le_rcg(dccg, inst, true);
}
// Don't program 0xF into the register field. Not valid since // K1 / K2 field is only 1 / 2 bits wide if (k1 == PIXEL_RATE_DIV_NA || k2 == PIXEL_RATE_DIV_NA) {
BREAK_TO_DEBUGGER(); return;
}
/* program OTG_PIXEL_RATE_DIV for DIVK1 and DIVK2 fields */
dccg35_set_pixel_rate_div(dccg, params->otg_inst, PIXEL_RATE_DIV_BY_1, PIXEL_RATE_DIV_BY_1);
/* The recommended programming sequence to enable DTBCLK DTO to generate * valid pixel HPO DPSTREAM ENCODER, specifies that DTO source select should * be set only after DTO is enabled. * PIPEx_DTO_SRC_SEL should not be programmed during DTBCLK update since OTG may still be on, and the * programming is handled in program_pix_clk() regardless, so it can be removed from here.
*/
} else { switch (params->otg_inst) { case 0:
REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, 0); break; case 1:
REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P1_GATE_DISABLE, 0); break; case 2:
REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P2_GATE_DISABLE, 0); break; case 3:
REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P3_GATE_DISABLE, 0); break;
}
/** * PIPEx_DTO_SRC_SEL should not be programmed during DTBCLK update since OTG may still be on, and the * programming is handled in program_pix_clk() regardless, so it can be removed from here.
*/
REG_UPDATE(OTG_PIXEL_RATE_CNTL[params->otg_inst],
DTBCLK_DTO_ENABLE[params->otg_inst], 0);
if (dccg->dpp_clock_gated[dpp_inst] == clock_on) return;
if (clock_on) {
dccg35_set_dppclk_rcg(dccg, dpp_inst, false);
/* turn off the DTO and leave phase/modulo at max */
dcn35_set_dppclk_enable(dccg, dpp_inst, 1);
REG_SET_2(DPPCLK_DTO_PARAM[dpp_inst], 0,
DPPCLK0_DTO_PHASE, 0xFF,
DPPCLK0_DTO_MODULO, 0xFF);
} else {
dcn35_set_dppclk_enable(dccg, dpp_inst, 0); /* turn on the DTO to generate a 0hz clock */
REG_SET_2(DPPCLK_DTO_PARAM[dpp_inst], 0,
DPPCLK0_DTO_PHASE, 0,
DPPCLK0_DTO_MODULO, 1); /*we have this in hwss: disable_plane*/ //dccg35_set_dppclk_rcg(dccg, dpp_inst, true);
}
/* set refclk as the source for symclk32_se */ switch (hpo_se_inst) { case 0:
REG_UPDATE_2(SYMCLK32_SE_CNTL,
SYMCLK32_SE0_SRC_SEL, 0,
SYMCLK32_SE0_EN, 0); if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) {
REG_UPDATE(DCCG_GATE_DISABLE_CNTL3,
SYMCLK32_SE0_GATE_DISABLE, 0); // REG_UPDATE(DCCG_GATE_DISABLE_CNTL3, // SYMCLK32_ROOT_SE0_GATE_DISABLE, 0);
} break; case 1:
REG_UPDATE_2(SYMCLK32_SE_CNTL,
SYMCLK32_SE1_SRC_SEL, 0,
SYMCLK32_SE1_EN, 0); if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) {
REG_UPDATE(DCCG_GATE_DISABLE_CNTL3,
SYMCLK32_SE1_GATE_DISABLE, 0); // REG_UPDATE(DCCG_GATE_DISABLE_CNTL3, // SYMCLK32_ROOT_SE1_GATE_DISABLE, 0);
} break; case 2:
REG_UPDATE_2(SYMCLK32_SE_CNTL,
SYMCLK32_SE2_SRC_SEL, 0,
SYMCLK32_SE2_EN, 0); if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) {
REG_UPDATE(DCCG_GATE_DISABLE_CNTL3,
SYMCLK32_SE2_GATE_DISABLE, 0); // REG_UPDATE(DCCG_GATE_DISABLE_CNTL3, // SYMCLK32_ROOT_SE2_GATE_DISABLE, 0);
} break; case 3:
REG_UPDATE_2(SYMCLK32_SE_CNTL,
SYMCLK32_SE3_SRC_SEL, 0,
SYMCLK32_SE3_EN, 0); if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) {
REG_UPDATE(DCCG_GATE_DISABLE_CNTL3,
SYMCLK32_SE3_GATE_DISABLE, 0); // REG_UPDATE(DCCG_GATE_DISABLE_CNTL3, // SYMCLK32_ROOT_SE3_GATE_DISABLE, 0);
} break; default:
BREAK_TO_DEBUGGER(); return;
}
}
staticvoid dccg35_init_cb(struct dccg *dccg)
{
(void)dccg; /* Any RCG should be done when driver enter low power mode*/
}
void dccg35_init(struct dccg *dccg)
{ int otg_inst; /* Set HPO stream encoder to use refclk to avoid case where PHY is * disabled and SYMCLK32 for HPO SE is sourced from PHYD32CLK which * will cause DCN to hang.
*/ for (otg_inst = 0; otg_inst < 4; otg_inst++)
dccg35_disable_symclk32_se(dccg, otg_inst);
if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) for (otg_inst = 0; otg_inst < 2; otg_inst++) {
dccg31_disable_symclk32_le(dccg, otg_inst);
dccg31_set_symclk32_le_root_clock_gating(dccg, otg_inst, false);
}
// if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // for (otg_inst = 0; otg_inst < 4; otg_inst++) // dccg35_disable_symclk_se(dccg, otg_inst, otg_inst);
if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpstream) for (otg_inst = 0; otg_inst < 4; otg_inst++) {
dccg35_set_dpstreamclk(dccg, REFCLK, otg_inst,
otg_inst);
dccg35_set_dpstreamclk_root_clock_gating(dccg, otg_inst, false);
}
for (i = 0; i < ARRAY_SIZE(fe_clk_en); i++) { if (fe_clk_en[i] && be_clk_sel[i] == link_enc_inst)
num_enabled_symclk_fe++;
} return num_enabled_symclk_fe;
}
switch (stream_enc_inst) { case 0:
REG_UPDATE_2(SYMCLKA_CLOCK_ENABLE,
SYMCLKA_FE_EN, 0,
SYMCLKA_FE_SRC_SEL, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_FE_ROOT_GATE_DISABLE, 0); break; case 1:
REG_UPDATE_2(SYMCLKB_CLOCK_ENABLE,
SYMCLKB_FE_EN, 0,
SYMCLKB_FE_SRC_SEL, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_FE_ROOT_GATE_DISABLE, 0); break; case 2:
REG_UPDATE_2(SYMCLKC_CLOCK_ENABLE,
SYMCLKC_FE_EN, 0,
SYMCLKC_FE_SRC_SEL, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_FE_ROOT_GATE_DISABLE, 0); break; case 3:
REG_UPDATE_2(SYMCLKD_CLOCK_ENABLE,
SYMCLKD_FE_EN, 0,
SYMCLKD_FE_SRC_SEL, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, 0); break; case 4:
REG_UPDATE_2(SYMCLKE_CLOCK_ENABLE,
SYMCLKE_FE_EN, 0,
SYMCLKE_FE_SRC_SEL, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_se) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_FE_ROOT_GATE_DISABLE, 0); break;
}
/*check other enabled symclk fe connected to this be */
num_enabled_symclk_fe = dccg35_get_number_enabled_symclk_fe_connected_to_be(dccg, link_enc_inst); /*only turn off backend clk if other front end attached to this backend are all off,
for mst, only turn off the backend if this is the last front end*/ if (num_enabled_symclk_fe == 0) { switch (link_enc_inst) { case 0:
REG_UPDATE(SYMCLKA_CLOCK_ENABLE,
SYMCLKA_CLOCK_ENABLE, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_ROOT_GATE_DISABLE, 0); break; case 1:
REG_UPDATE(SYMCLKB_CLOCK_ENABLE,
SYMCLKB_CLOCK_ENABLE, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_ROOT_GATE_DISABLE, 0); break; case 2:
REG_UPDATE(SYMCLKC_CLOCK_ENABLE,
SYMCLKC_CLOCK_ENABLE, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_ROOT_GATE_DISABLE, 0); break; case 3:
REG_UPDATE(SYMCLKD_CLOCK_ENABLE,
SYMCLKD_CLOCK_ENABLE, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, 0); break; case 4:
REG_UPDATE(SYMCLKE_CLOCK_ENABLE,
SYMCLKE_CLOCK_ENABLE, 0); // if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) // REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_ROOT_GATE_DISABLE, 0); break;
}
}
}
staticvoid dccg35_set_dpstreamclk_cb( struct dccg *dccg, enum streamclk_source src, int otg_inst, int dp_hpo_inst)
{
staticvoid dccg35_set_dpstreamclk_root_clock_gating_cb( struct dccg *dccg, int dp_hpo_inst, bool power_on)
{ /* power_on set indicates we need to ungate * Currently called from optimize_bandwidth and prepare_bandwidth calls * Since clock source is not passed restore to refclock on ungate * Instance 0 is implied here since only one streamclock resource * Redundant as gating when enabled is acheived through set_dpstreamclk
*/ if (power_on)
dccg35_enable_dpstreamclk_new(dccg,
DP_STREAM_REFCLK,
dp_hpo_inst); else
dccg35_disable_dpstreamclk_new(dccg, dp_hpo_inst);
}
staticvoid dccg35_update_dpp_dto_cb(struct dccg *dccg, int dpp_inst, int req_dppclk)
{ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
if (dccg->dpp_clock_gated[dpp_inst]) { /* * Do not update the DPPCLK DTO if the clock is stopped.
*/ return;
}
if (dccg->ref_dppclk && req_dppclk) { int ref_dppclk = dccg->ref_dppclk; int modulo, phase;
// phase / modulo = dpp pipe clk / dpp global clk
modulo = 0xff; // use FF at the end
phase = ((modulo * req_dppclk) + ref_dppclk - 1) / ref_dppclk;
if (phase > 0xff) {
ASSERT(false);
phase = 0xff;
}
staticvoid dccg35_dpp_root_clock_control_cb( struct dccg *dccg, unsignedint dpp_inst, bool power_on)
{ if (dccg->dpp_clock_gated[dpp_inst] == power_on) return; /* power_on set indicates we need to ungate * Currently called from optimize_bandwidth and prepare_bandwidth calls * Since clock source is not passed restore to refclock on ungate * Redundant as gating when enabled is acheived through update_dpp_dto
*/
dccg35_set_dppclk_rcg(dccg, dpp_inst, !power_on);
staticvoid dccg35_disable_symclk32_le_cb(struct dccg *dccg, int inst)
{
dccg35_disable_symclk32_le_new(dccg, inst);
}
staticvoid dccg35_set_symclk32_le_root_clock_gating_cb( struct dccg *dccg, int inst, bool power_on)
{ /* power_on set indicates we need to ungate * Currently called from optimize_bandwidth and prepare_bandwidth calls * Since clock source is not passed restore to refclock on ungate * Redundant as gating when enabled is acheived through disable_symclk32_le
*/ if (power_on)
dccg35_enable_symclk32_le_new(dccg, inst, SYMCLK32_LE_REFCLK); else
dccg35_disable_symclk32_le_new(dccg, inst);
}
staticvoid dccg35_set_physymclk_cb( struct dccg *dccg, int inst, enum physymclk_clock_source clk_src, bool force_enable)
{ /* force_enable = 0 indicates we can switch to ref clock */ if (force_enable)
dccg35_enable_physymclk_new(dccg, inst, (enum physymclk_source)clk_src); else
dccg35_disable_physymclk_new(dccg, inst);
}
staticvoid dccg35_set_physymclk_root_clock_gating_cb( struct dccg *dccg, int inst, bool power_on)
{ /* Redundant RCG already done in disable_physymclk * power_on = 1 indicates we need to ungate
*/ if (power_on)
dccg35_enable_physymclk_new(dccg, inst, PHYSYMCLK_REFCLK); else
dccg35_disable_physymclk_new(dccg, inst);
}
staticvoid dccg35_set_symclk32_le_root_clock_gating( struct dccg *dccg, int inst, bool power_on)
{ /* power_on set indicates we need to ungate * Currently called from optimize_bandwidth and prepare_bandwidth calls * Since clock source is not passed restore to refclock on ungate * Redundant as gating when enabled is acheived through disable_symclk32_le
*/ if (power_on)
dccg35_enable_symclk32_le_new(dccg, inst, SYMCLK32_LE_REFCLK); else
dccg35_disable_symclk32_le_new(dccg, inst);
}
/* program OTG_PIXEL_RATE_DIV for DIVK1 and DIVK2 fields */
dccg35_set_pixel_rate_div(dccg, params->otg_inst, PIXEL_RATE_DIV_BY_1, PIXEL_RATE_DIV_BY_1);
/* The recommended programming sequence to enable DTBCLK DTO to generate * valid pixel HPO DPSTREAM ENCODER, specifies that DTO source select should * be set only after DTO is enabled
*/
REG_UPDATE(OTG_PIXEL_RATE_CNTL[params->otg_inst],
PIPE_DTO_SRC_SEL[params->otg_inst], 2);
} else {
dccg35_disable_dtbclk_p_new(dccg, params->otg_inst);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.