1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2024 Advanced Micro Devices, Inc.
4 
5 #include "dml2_mcg_dcn4.h"
6 #include "dml_top_soc_parameter_types.h"
7 
8 static bool build_min_clock_table(const struct dml2_soc_bb *soc_bb, struct dml2_mcg_min_clock_table *min_table);
9 
mcg_dcn4_build_min_clock_table(struct dml2_mcg_build_min_clock_table_params_in_out * in_out)10 bool mcg_dcn4_build_min_clock_table(struct dml2_mcg_build_min_clock_table_params_in_out *in_out)
11 {
12 	return build_min_clock_table(in_out->soc_bb, in_out->min_clk_table);
13 }
14 
uclk_to_dram_bw_kbps(unsigned long uclk_khz,const struct dml2_dram_params * dram_config)15 static unsigned long long uclk_to_dram_bw_kbps(unsigned long uclk_khz, const struct dml2_dram_params *dram_config)
16 {
17 	unsigned long long bw_kbps = 0;
18 
19 	bw_kbps = (unsigned long long) uclk_khz * dram_config->channel_count * dram_config->channel_width_bytes * dram_config->transactions_per_clock;
20 
21 	return bw_kbps;
22 }
23 
round_up_to_quantized_values(unsigned long value,const unsigned long * quantized_values,int num_quantized_values)24 static unsigned long round_up_to_quantized_values(unsigned long value, const unsigned long *quantized_values, int num_quantized_values)
25 {
26 	int i;
27 
28 	if (!quantized_values)
29 		return 0;
30 
31 	for (i = 0; i < num_quantized_values; i++) {
32 		if (quantized_values[i] > value)
33 			return quantized_values[i];
34 	}
35 
36 	return 0;
37 }
38 
build_min_clk_table_fine_grained(const struct dml2_soc_bb * soc_bb,struct dml2_mcg_min_clock_table * min_table)39 static bool build_min_clk_table_fine_grained(const struct dml2_soc_bb *soc_bb, struct dml2_mcg_min_clock_table *min_table)
40 {
41 	bool dcfclk_fine_grained = false, fclk_fine_grained = false;
42 
43 	int i;
44 	unsigned int j;
45 
46 	unsigned long min_dcfclk_khz = 0;
47 	unsigned long min_fclk_khz = 0;
48 	unsigned long prev_100, cur_50;
49 
50 	if (soc_bb->clk_table.dcfclk.num_clk_values == 2) {
51 		dcfclk_fine_grained = true;
52 	}
53 
54 	if (soc_bb->clk_table.fclk.num_clk_values == 2) {
55 		fclk_fine_grained = true;
56 	}
57 
58 	min_dcfclk_khz = soc_bb->clk_table.dcfclk.clk_values_khz[0];
59 	min_fclk_khz = soc_bb->clk_table.fclk.clk_values_khz[0];
60 
61 	// First calculate the table for "balanced" bandwidths across UCLK/FCLK
62 	for (i = 0; i < soc_bb->clk_table.uclk.num_clk_values; i++) {
63 		min_table->dram_bw_table.entries[i].pre_derate_dram_bw_kbps = uclk_to_dram_bw_kbps(soc_bb->clk_table.uclk.clk_values_khz[i], &soc_bb->clk_table.dram_config);
64 
65 		min_table->dram_bw_table.entries[i].min_fclk_khz = (unsigned long)((((double)min_table->dram_bw_table.entries[i].pre_derate_dram_bw_kbps * soc_bb->qos_parameters.derate_table.system_active_urgent.dram_derate_percent_pixel / 100) / ((double)soc_bb->qos_parameters.derate_table.system_active_urgent.fclk_derate_percent / 100)) / soc_bb->fabric_datapath_to_dcn_data_return_bytes);
66 	}
67 	min_table->dram_bw_table.num_entries = soc_bb->clk_table.uclk.num_clk_values;
68 
69 	// To create the minium table, effectively shift "up" all the dcfclk/fclk entries by 1, and then replace the lowest entry with min fclk/dcfclk
70 	for (i = min_table->dram_bw_table.num_entries - 1; i > 0; i--) {
71 		prev_100 = min_table->dram_bw_table.entries[i - 1].min_fclk_khz;
72 		cur_50 = min_table->dram_bw_table.entries[i].min_fclk_khz / 2;
73 		min_table->dram_bw_table.entries[i].min_fclk_khz = prev_100 > cur_50 ? prev_100 : cur_50;
74 
75 		if (!fclk_fine_grained) {
76 			min_table->dram_bw_table.entries[i].min_fclk_khz = round_up_to_quantized_values(min_table->dram_bw_table.entries[i].min_fclk_khz, soc_bb->clk_table.fclk.clk_values_khz, soc_bb->clk_table.fclk.num_clk_values);
77 		}
78 	}
79 	min_table->dram_bw_table.entries[0].min_fclk_khz /= 2;
80 
81 	// Clamp to minimums and maximums
82 	for (i = 0; i < (int)min_table->dram_bw_table.num_entries; i++) {
83 		if (min_table->dram_bw_table.entries[i].min_dcfclk_khz < min_dcfclk_khz)
84 			min_table->dram_bw_table.entries[i].min_dcfclk_khz = min_dcfclk_khz;
85 
86 		if (min_table->dram_bw_table.entries[i].min_fclk_khz < min_fclk_khz)
87 			min_table->dram_bw_table.entries[i].min_fclk_khz = min_fclk_khz;
88 
89 		if (soc_bb->max_fclk_for_uclk_dpm_khz > 0 &&
90 			min_table->dram_bw_table.entries[i].min_fclk_khz > soc_bb->max_fclk_for_uclk_dpm_khz)
91 			min_table->dram_bw_table.entries[i].min_fclk_khz = soc_bb->max_fclk_for_uclk_dpm_khz;
92 
93 		min_table->dram_bw_table.entries[i].min_dcfclk_khz =
94 			min_table->dram_bw_table.entries[i].min_fclk_khz *
95 			soc_bb->qos_parameters.derate_table.system_active_urgent.fclk_derate_percent / soc_bb->qos_parameters.derate_table.system_active_urgent.dcfclk_derate_percent;
96 
97 		min_table->dram_bw_table.entries[i].min_dcfclk_khz =
98 			min_table->dram_bw_table.entries[i].min_dcfclk_khz * soc_bb->fabric_datapath_to_dcn_data_return_bytes / soc_bb->return_bus_width_bytes;
99 
100 		if (!dcfclk_fine_grained) {
101 			min_table->dram_bw_table.entries[i].min_dcfclk_khz = round_up_to_quantized_values(min_table->dram_bw_table.entries[i].min_dcfclk_khz, soc_bb->clk_table.dcfclk.clk_values_khz, soc_bb->clk_table.dcfclk.num_clk_values);
102 		}
103 	}
104 
105 	// Prune states which are invalid (some clocks exceed maximum)
106 	for (i = 0; i < (int)min_table->dram_bw_table.num_entries; i++) {
107 		if (min_table->dram_bw_table.entries[i].min_dcfclk_khz > min_table->max_clocks_khz.dcfclk ||
108 			min_table->dram_bw_table.entries[i].min_fclk_khz > min_table->max_clocks_khz.fclk) {
109 			min_table->dram_bw_table.num_entries = i;
110 			break;
111 		}
112 	}
113 
114 	// Prune duplicate states
115 	for (i = 0; i < (int)min_table->dram_bw_table.num_entries - 1; i++) {
116 		if (min_table->dram_bw_table.entries[i].min_dcfclk_khz == min_table->dram_bw_table.entries[i + 1].min_dcfclk_khz &&
117 			min_table->dram_bw_table.entries[i].min_fclk_khz == min_table->dram_bw_table.entries[i + 1].min_fclk_khz &&
118 			min_table->dram_bw_table.entries[i].pre_derate_dram_bw_kbps == min_table->dram_bw_table.entries[i + 1].pre_derate_dram_bw_kbps) {
119 
120 			// i + 1 is the same state as i, so shift everything
121 			for (j = i + 1; j < min_table->dram_bw_table.num_entries; j++) {
122 				min_table->dram_bw_table.entries[j].min_dcfclk_khz = min_table->dram_bw_table.entries[j + 1].min_dcfclk_khz;
123 				min_table->dram_bw_table.entries[j].min_fclk_khz = min_table->dram_bw_table.entries[j + 1].min_fclk_khz;
124 				min_table->dram_bw_table.entries[j].pre_derate_dram_bw_kbps = min_table->dram_bw_table.entries[j + 1].pre_derate_dram_bw_kbps;
125 			}
126 			min_table->dram_bw_table.num_entries--;
127 		}
128 	}
129 
130 	return true;
131 }
132 
build_min_clk_table_coarse_grained(const struct dml2_soc_bb * soc_bb,struct dml2_mcg_min_clock_table * min_table)133 static bool build_min_clk_table_coarse_grained(const struct dml2_soc_bb *soc_bb, struct dml2_mcg_min_clock_table *min_table)
134 {
135 	int i;
136 
137 	for (i = 0; i < soc_bb->clk_table.uclk.num_clk_values; i++) {
138 		min_table->dram_bw_table.entries[i].pre_derate_dram_bw_kbps = uclk_to_dram_bw_kbps(soc_bb->clk_table.uclk.clk_values_khz[i], &soc_bb->clk_table.dram_config);
139 		min_table->dram_bw_table.entries[i].min_dcfclk_khz = soc_bb->clk_table.dcfclk.clk_values_khz[i];
140 		min_table->dram_bw_table.entries[i].min_fclk_khz = soc_bb->clk_table.fclk.clk_values_khz[i];
141 	}
142 	min_table->dram_bw_table.num_entries = soc_bb->clk_table.uclk.num_clk_values;
143 
144 	return true;
145 }
146 
build_min_clock_table(const struct dml2_soc_bb * soc_bb,struct dml2_mcg_min_clock_table * min_table)147 static bool build_min_clock_table(const struct dml2_soc_bb *soc_bb, struct dml2_mcg_min_clock_table *min_table)
148 {
149 	bool result;
150 	bool dcfclk_fine_grained = false, fclk_fine_grained = false, clock_state_count_equal = false;
151 
152 	if (!soc_bb || !min_table)
153 		return false;
154 
155 	if (soc_bb->clk_table.dcfclk.num_clk_values < 2 || soc_bb->clk_table.fclk.num_clk_values < 2)
156 		return false;
157 
158 	if (soc_bb->clk_table.uclk.num_clk_values > DML_MCG_MAX_CLK_TABLE_SIZE)
159 		return false;
160 
161 	if (soc_bb->clk_table.dcfclk.num_clk_values == 2) {
162 		dcfclk_fine_grained = true;
163 	}
164 
165 	if (soc_bb->clk_table.fclk.num_clk_values == 2) {
166 		fclk_fine_grained = true;
167 	}
168 
169 	if (soc_bb->clk_table.fclk.num_clk_values == soc_bb->clk_table.dcfclk.num_clk_values &&
170 		soc_bb->clk_table.fclk.num_clk_values == soc_bb->clk_table.uclk.num_clk_values)
171 		clock_state_count_equal = true;
172 
173 	min_table->fixed_clocks_khz.amclk = 0;
174 	min_table->fixed_clocks_khz.dprefclk = soc_bb->dprefclk_mhz * 1000;
175 	min_table->fixed_clocks_khz.pcierefclk = soc_bb->pcie_refclk_mhz * 1000;
176 	min_table->fixed_clocks_khz.dchubrefclk = soc_bb->dchub_refclk_mhz * 1000;
177 	min_table->fixed_clocks_khz.xtalclk = soc_bb->xtalclk_mhz * 1000;
178 
179 	min_table->max_clocks_khz.dispclk = soc_bb->clk_table.dispclk.clk_values_khz[soc_bb->clk_table.dispclk.num_clk_values - 1];
180 	min_table->max_clocks_khz.dppclk = soc_bb->clk_table.dppclk.clk_values_khz[soc_bb->clk_table.dppclk.num_clk_values - 1];
181 	min_table->max_clocks_khz.dscclk = soc_bb->clk_table.dscclk.clk_values_khz[soc_bb->clk_table.dscclk.num_clk_values - 1];
182 	min_table->max_clocks_khz.dtbclk = soc_bb->clk_table.dtbclk.clk_values_khz[soc_bb->clk_table.dtbclk.num_clk_values - 1];
183 	min_table->max_clocks_khz.phyclk = soc_bb->clk_table.phyclk.clk_values_khz[soc_bb->clk_table.phyclk.num_clk_values - 1];
184 
185 	min_table->max_clocks_khz.dcfclk = soc_bb->clk_table.dcfclk.clk_values_khz[soc_bb->clk_table.dcfclk.num_clk_values - 1];
186 	min_table->max_clocks_khz.fclk = soc_bb->clk_table.fclk.clk_values_khz[soc_bb->clk_table.fclk.num_clk_values - 1];
187 
188 	if (dcfclk_fine_grained || fclk_fine_grained || !clock_state_count_equal)
189 		result = build_min_clk_table_fine_grained(soc_bb, min_table);
190 	else
191 		result = build_min_clk_table_coarse_grained(soc_bb, min_table);
192 
193 	return result;
194 }
195