1/* $NetBSD: amdgpu_rv1_clk_mgr.c,v 1.2 2021/12/18 23:45:02 riastradh Exp $ */ 2 3/* 4 * Copyright 2018 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: AMD 25 * 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: amdgpu_rv1_clk_mgr.c,v 1.2 2021/12/18 23:45:02 riastradh Exp $"); 30 31#include <linux/slab.h> 32 33#include "reg_helper.h" 34#include "core_types.h" 35#include "clk_mgr_internal.h" 36#include "rv1_clk_mgr.h" 37#include "dce100/dce_clk_mgr.h" 38#include "dce112/dce112_clk_mgr.h" 39#include "rv1_clk_mgr_vbios_smu.h" 40#include "rv1_clk_mgr_clk.h" 41 42void rv1_init_clocks(struct clk_mgr *clk_mgr) 43{ 44 memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks)); 45} 46 47static int rv1_determine_dppclk_threshold(struct clk_mgr_internal *clk_mgr, struct dc_clocks *new_clocks) 48{ 49 bool request_dpp_div = new_clocks->dispclk_khz > new_clocks->dppclk_khz; 50 bool dispclk_increase = new_clocks->dispclk_khz > clk_mgr->base.clks.dispclk_khz; 51 int disp_clk_threshold = new_clocks->max_supported_dppclk_khz; 52 bool cur_dpp_div = clk_mgr->base.clks.dispclk_khz > clk_mgr->base.clks.dppclk_khz; 53 54 /* increase clock, looking for div is 0 for current, request div is 1*/ 55 if (dispclk_increase) { 56 /* already divided by 2, no need to reach target clk with 2 steps*/ 57 if (cur_dpp_div) 58 return new_clocks->dispclk_khz; 59 60 /* request disp clk is lower than maximum supported dpp clk, 61 * no need to reach target clk with two steps. 62 */ 63 if (new_clocks->dispclk_khz <= disp_clk_threshold) 64 return new_clocks->dispclk_khz; 65 66 /* target dpp clk not request divided by 2, still within threshold */ 67 if (!request_dpp_div) 68 return new_clocks->dispclk_khz; 69 70 } else { 71 /* decrease clock, looking for current dppclk divided by 2, 72 * request dppclk not divided by 2. 73 */ 74 75 /* current dpp clk not divided by 2, no need to ramp*/ 76 if (!cur_dpp_div) 77 return new_clocks->dispclk_khz; 78 79 /* current disp clk is lower than current maximum dpp clk, 80 * no need to ramp 81 */ 82 if (clk_mgr->base.clks.dispclk_khz <= disp_clk_threshold) 83 return new_clocks->dispclk_khz; 84 85 /* request dpp clk need to be divided by 2 */ 86 if (request_dpp_div) 87 return new_clocks->dispclk_khz; 88 } 89 90 return disp_clk_threshold; 91} 92 93static void ramp_up_dispclk_with_dpp(struct clk_mgr_internal *clk_mgr, struct dc *dc, struct dc_clocks *new_clocks) 94{ 95 int i; 96 int dispclk_to_dpp_threshold = rv1_determine_dppclk_threshold(clk_mgr, new_clocks); 97 bool request_dpp_div = new_clocks->dispclk_khz > new_clocks->dppclk_khz; 98 99 /* set disp clk to dpp clk threshold */ 100 101 clk_mgr->funcs->set_dispclk(clk_mgr, dispclk_to_dpp_threshold); 102 clk_mgr->funcs->set_dprefclk(clk_mgr); 103 104 105 /* update request dpp clk division option */ 106 for (i = 0; i < dc->res_pool->pipe_count; i++) { 107 struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; 108 109 if (!pipe_ctx->plane_state) 110 continue; 111 112 pipe_ctx->plane_res.dpp->funcs->dpp_dppclk_control( 113 pipe_ctx->plane_res.dpp, 114 request_dpp_div, 115 true); 116 } 117 118 /* If target clk not same as dppclk threshold, set to target clock */ 119 if (dispclk_to_dpp_threshold != new_clocks->dispclk_khz) { 120 clk_mgr->funcs->set_dispclk(clk_mgr, new_clocks->dispclk_khz); 121 clk_mgr->funcs->set_dprefclk(clk_mgr); 122 } 123 124 125 clk_mgr->base.clks.dispclk_khz = new_clocks->dispclk_khz; 126 clk_mgr->base.clks.dppclk_khz = new_clocks->dppclk_khz; 127 clk_mgr->base.clks.max_supported_dppclk_khz = new_clocks->max_supported_dppclk_khz; 128} 129 130static void rv1_update_clocks(struct clk_mgr *clk_mgr_base, 131 struct dc_state *context, 132 bool safe_to_lower) 133{ 134 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 135 struct dc *dc = clk_mgr_base->ctx->dc; 136 struct dc_debug_options *debug = &dc->debug; 137 struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk; 138 struct pp_smu_funcs_rv *pp_smu = NULL; 139 bool send_request_to_increase = false; 140 bool send_request_to_lower = false; 141 int display_count; 142 143 bool enter_display_off = false; 144 145 ASSERT(clk_mgr->pp_smu); 146 147 if (dc->work_arounds.skip_clock_update) 148 return; 149 150 pp_smu = &clk_mgr->pp_smu->rv_funcs; 151 152 display_count = clk_mgr_helper_get_active_display_cnt(dc, context); 153 154 if (display_count == 0) 155 enter_display_off = true; 156 157 if (enter_display_off == safe_to_lower) { 158 /* 159 * Notify SMU active displays 160 * if function pointer not set up, this message is 161 * sent as part of pplib_apply_display_requirements. 162 */ 163 if (pp_smu->set_display_count) 164 pp_smu->set_display_count(&pp_smu->pp_smu, display_count); 165 } 166 167 if (new_clocks->dispclk_khz > clk_mgr_base->clks.dispclk_khz 168 || new_clocks->phyclk_khz > clk_mgr_base->clks.phyclk_khz 169 || new_clocks->fclk_khz > clk_mgr_base->clks.fclk_khz 170 || new_clocks->dcfclk_khz > clk_mgr_base->clks.dcfclk_khz) 171 send_request_to_increase = true; 172 173 if (should_set_clock(safe_to_lower, new_clocks->phyclk_khz, clk_mgr_base->clks.phyclk_khz)) { 174 clk_mgr_base->clks.phyclk_khz = new_clocks->phyclk_khz; 175 send_request_to_lower = true; 176 } 177 178 // F Clock 179 if (debug->force_fclk_khz != 0) 180 new_clocks->fclk_khz = debug->force_fclk_khz; 181 182 if (should_set_clock(safe_to_lower, new_clocks->fclk_khz, clk_mgr_base->clks.fclk_khz)) { 183 clk_mgr_base->clks.fclk_khz = new_clocks->fclk_khz; 184 send_request_to_lower = true; 185 } 186 187 //DCF Clock 188 if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr_base->clks.dcfclk_khz)) { 189 clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz; 190 send_request_to_lower = true; 191 } 192 193 if (should_set_clock(safe_to_lower, 194 new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) { 195 clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz; 196 send_request_to_lower = true; 197 } 198 199 /* make sure dcf clk is before dpp clk to 200 * make sure we have enough voltage to run dpp clk 201 */ 202 if (send_request_to_increase) { 203 /*use dcfclk to request voltage*/ 204 if (pp_smu->set_hard_min_fclk_by_freq && 205 pp_smu->set_hard_min_dcfclk_by_freq && 206 pp_smu->set_min_deep_sleep_dcfclk) { 207 pp_smu->set_hard_min_fclk_by_freq(&pp_smu->pp_smu, new_clocks->fclk_khz / 1000); 208 pp_smu->set_hard_min_dcfclk_by_freq(&pp_smu->pp_smu, new_clocks->dcfclk_khz / 1000); 209 pp_smu->set_min_deep_sleep_dcfclk(&pp_smu->pp_smu, (new_clocks->dcfclk_deep_sleep_khz + 999) / 1000); 210 } 211 } 212 213 /* dcn1 dppclk is tied to dispclk */ 214 /* program dispclk on = as a w/a for sleep resume clock ramping issues */ 215 if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz) 216 || new_clocks->dispclk_khz == clk_mgr_base->clks.dispclk_khz) { 217 ramp_up_dispclk_with_dpp(clk_mgr, dc, new_clocks); 218 clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; 219 send_request_to_lower = true; 220 } 221 222 if (!send_request_to_increase && send_request_to_lower) { 223 /*use dcfclk to request voltage*/ 224 if (pp_smu->set_hard_min_fclk_by_freq && 225 pp_smu->set_hard_min_dcfclk_by_freq && 226 pp_smu->set_min_deep_sleep_dcfclk) { 227 pp_smu->set_hard_min_fclk_by_freq(&pp_smu->pp_smu, new_clocks->fclk_khz / 1000); 228 pp_smu->set_hard_min_dcfclk_by_freq(&pp_smu->pp_smu, new_clocks->dcfclk_khz / 1000); 229 pp_smu->set_min_deep_sleep_dcfclk(&pp_smu->pp_smu, (new_clocks->dcfclk_deep_sleep_khz + 999) / 1000); 230 } 231 } 232} 233 234static void rv1_enable_pme_wa(struct clk_mgr *clk_mgr_base) 235{ 236 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 237 struct pp_smu_funcs_rv *pp_smu = NULL; 238 239 if (clk_mgr->pp_smu) { 240 pp_smu = &clk_mgr->pp_smu->rv_funcs; 241 242 if (pp_smu->set_pme_wa_enable) 243 pp_smu->set_pme_wa_enable(&pp_smu->pp_smu); 244 } 245} 246 247static struct clk_mgr_funcs rv1_clk_funcs = { 248 .init_clocks = rv1_init_clocks, 249 .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, 250 .update_clocks = rv1_update_clocks, 251 .enable_pme_wa = rv1_enable_pme_wa, 252}; 253 254static struct clk_mgr_internal_funcs rv1_clk_internal_funcs = { 255 .set_dispclk = rv1_vbios_smu_set_dispclk, 256 .set_dprefclk = dce112_set_dprefclk 257}; 258 259void rv1_clk_mgr_construct(struct dc_context *ctx, struct clk_mgr_internal *clk_mgr, struct pp_smu_funcs *pp_smu) 260{ 261 struct dc_debug_options *debug = &ctx->dc->debug; 262 struct dc_bios *bp = ctx->dc_bios; 263 264 clk_mgr->base.ctx = ctx; 265 clk_mgr->pp_smu = pp_smu; 266 clk_mgr->base.funcs = &rv1_clk_funcs; 267 clk_mgr->funcs = &rv1_clk_internal_funcs; 268 269 clk_mgr->dfs_bypass_disp_clk = 0; 270 271 clk_mgr->dprefclk_ss_percentage = 0; 272 clk_mgr->dprefclk_ss_divider = 1000; 273 clk_mgr->ss_on_dprefclk = false; 274 clk_mgr->base.dprefclk_khz = 600000; 275 276 if (bp->integrated_info) 277 clk_mgr->base.dentist_vco_freq_khz = bp->integrated_info->dentist_vco_freq; 278 if (bp->fw_info_valid && clk_mgr->base.dentist_vco_freq_khz == 0) { 279 clk_mgr->base.dentist_vco_freq_khz = bp->fw_info.smu_gpu_pll_output_freq; 280 if (clk_mgr->base.dentist_vco_freq_khz == 0) 281 clk_mgr->base.dentist_vco_freq_khz = 3600000; 282 } 283 284 if (!debug->disable_dfs_bypass && bp->integrated_info) 285 if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) 286 clk_mgr->dfs_bypass_enabled = true; 287 288 dce_clock_read_ss_info(clk_mgr); 289} 290 291 292