1 | /* |
2 | * Copyright 2021 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | */ |
22 | |
23 | #include "amdgpu.h" |
24 | #include "amdgpu_i2c.h" |
25 | #include "amdgpu_atombios.h" |
26 | #include "atom.h" |
27 | #include "amd_pcie.h" |
28 | #include "legacy_dpm.h" |
29 | #include "amdgpu_dpm_internal.h" |
30 | #include "amdgpu_display.h" |
31 | |
32 | #define amdgpu_dpm_pre_set_power_state(adev) \ |
33 | ((adev)->powerplay.pp_funcs->pre_set_power_state((adev)->powerplay.pp_handle)) |
34 | |
35 | #define amdgpu_dpm_post_set_power_state(adev) \ |
36 | ((adev)->powerplay.pp_funcs->post_set_power_state((adev)->powerplay.pp_handle)) |
37 | |
38 | #define amdgpu_dpm_display_configuration_changed(adev) \ |
39 | ((adev)->powerplay.pp_funcs->display_configuration_changed((adev)->powerplay.pp_handle)) |
40 | |
41 | #define amdgpu_dpm_print_power_state(adev, ps) \ |
42 | ((adev)->powerplay.pp_funcs->print_power_state((adev)->powerplay.pp_handle, (ps))) |
43 | |
44 | #define amdgpu_dpm_vblank_too_short(adev) \ |
45 | ((adev)->powerplay.pp_funcs->vblank_too_short((adev)->powerplay.pp_handle)) |
46 | |
47 | #define amdgpu_dpm_check_state_equal(adev, cps, rps, equal) \ |
48 | ((adev)->powerplay.pp_funcs->check_state_equal((adev)->powerplay.pp_handle, (cps), (rps), (equal))) |
49 | |
50 | void amdgpu_dpm_print_class_info(u32 class, u32 class2) |
51 | { |
52 | const char *s; |
53 | |
54 | switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) { |
55 | case ATOM_PPLIB_CLASSIFICATION_UI_NONE: |
56 | default: |
57 | s = "none" ; |
58 | break; |
59 | case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY: |
60 | s = "battery" ; |
61 | break; |
62 | case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED: |
63 | s = "balanced" ; |
64 | break; |
65 | case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE: |
66 | s = "performance" ; |
67 | break; |
68 | } |
69 | printk("\tui class: %s\n" , s); |
70 | printk("\tinternal class:" ); |
71 | if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) && |
72 | (class2 == 0)) |
73 | pr_cont(" none" ); |
74 | else { |
75 | if (class & ATOM_PPLIB_CLASSIFICATION_BOOT) |
76 | pr_cont(" boot" ); |
77 | if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL) |
78 | pr_cont(" thermal" ); |
79 | if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE) |
80 | pr_cont(" limited_pwr" ); |
81 | if (class & ATOM_PPLIB_CLASSIFICATION_REST) |
82 | pr_cont(" rest" ); |
83 | if (class & ATOM_PPLIB_CLASSIFICATION_FORCED) |
84 | pr_cont(" forced" ); |
85 | if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) |
86 | pr_cont(" 3d_perf" ); |
87 | if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE) |
88 | pr_cont(" ovrdrv" ); |
89 | if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) |
90 | pr_cont(" uvd" ); |
91 | if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW) |
92 | pr_cont(" 3d_low" ); |
93 | if (class & ATOM_PPLIB_CLASSIFICATION_ACPI) |
94 | pr_cont(" acpi" ); |
95 | if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) |
96 | pr_cont(" uvd_hd2" ); |
97 | if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) |
98 | pr_cont(" uvd_hd" ); |
99 | if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) |
100 | pr_cont(" uvd_sd" ); |
101 | if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2) |
102 | pr_cont(" limited_pwr2" ); |
103 | if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) |
104 | pr_cont(" ulv" ); |
105 | if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) |
106 | pr_cont(" uvd_mvc" ); |
107 | } |
108 | pr_cont("\n" ); |
109 | } |
110 | |
111 | void amdgpu_dpm_print_cap_info(u32 caps) |
112 | { |
113 | printk("\tcaps:" ); |
114 | if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) |
115 | pr_cont(" single_disp" ); |
116 | if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK) |
117 | pr_cont(" video" ); |
118 | if (caps & ATOM_PPLIB_DISALLOW_ON_DC) |
119 | pr_cont(" no_dc" ); |
120 | pr_cont("\n" ); |
121 | } |
122 | |
123 | void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, |
124 | struct amdgpu_ps *rps) |
125 | { |
126 | printk("\tstatus:" ); |
127 | if (rps == adev->pm.dpm.current_ps) |
128 | pr_cont(" c" ); |
129 | if (rps == adev->pm.dpm.requested_ps) |
130 | pr_cont(" r" ); |
131 | if (rps == adev->pm.dpm.boot_ps) |
132 | pr_cont(" b" ); |
133 | pr_cont("\n" ); |
134 | } |
135 | |
136 | void amdgpu_pm_print_power_states(struct amdgpu_device *adev) |
137 | { |
138 | int i; |
139 | |
140 | if (adev->powerplay.pp_funcs->print_power_state == NULL) |
141 | return; |
142 | |
143 | for (i = 0; i < adev->pm.dpm.num_ps; i++) |
144 | amdgpu_dpm_print_power_state(adev, &adev->pm.dpm.ps[i]); |
145 | |
146 | } |
147 | |
148 | union power_info { |
149 | struct _ATOM_POWERPLAY_INFO info; |
150 | struct _ATOM_POWERPLAY_INFO_V2 info_2; |
151 | struct _ATOM_POWERPLAY_INFO_V3 info_3; |
152 | struct _ATOM_PPLIB_POWERPLAYTABLE pplib; |
153 | struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; |
154 | struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; |
155 | struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; |
156 | struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; |
157 | }; |
158 | |
159 | int amdgpu_get_platform_caps(struct amdgpu_device *adev) |
160 | { |
161 | struct amdgpu_mode_info *mode_info = &adev->mode_info; |
162 | union power_info *power_info; |
163 | int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); |
164 | u16 data_offset; |
165 | u8 frev, crev; |
166 | |
167 | if (!amdgpu_atom_parse_data_header(ctx: mode_info->atom_context, index, NULL, |
168 | frev: &frev, crev: &crev, data_start: &data_offset)) |
169 | return -EINVAL; |
170 | power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); |
171 | |
172 | adev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); |
173 | adev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); |
174 | adev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | union fan_info { |
180 | struct _ATOM_PPLIB_FANTABLE fan; |
181 | struct _ATOM_PPLIB_FANTABLE2 fan2; |
182 | struct _ATOM_PPLIB_FANTABLE3 fan3; |
183 | }; |
184 | |
185 | static int amdgpu_parse_clk_voltage_dep_table(struct amdgpu_clock_voltage_dependency_table *amdgpu_table, |
186 | ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table) |
187 | { |
188 | u32 size = atom_table->ucNumEntries * |
189 | sizeof(struct amdgpu_clock_voltage_dependency_entry); |
190 | int i; |
191 | ATOM_PPLIB_Clock_Voltage_Dependency_Record *entry; |
192 | |
193 | amdgpu_table->entries = kzalloc(size, GFP_KERNEL); |
194 | if (!amdgpu_table->entries) |
195 | return -ENOMEM; |
196 | |
197 | entry = &atom_table->entries[0]; |
198 | for (i = 0; i < atom_table->ucNumEntries; i++) { |
199 | amdgpu_table->entries[i].clk = le16_to_cpu(entry->usClockLow) | |
200 | (entry->ucClockHigh << 16); |
201 | amdgpu_table->entries[i].v = le16_to_cpu(entry->usVoltage); |
202 | entry = (ATOM_PPLIB_Clock_Voltage_Dependency_Record *) |
203 | ((u8 *)entry + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Record)); |
204 | } |
205 | amdgpu_table->count = atom_table->ucNumEntries; |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | /* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */ |
211 | #define 12 |
212 | #define 14 |
213 | #define 16 |
214 | #define 18 |
215 | #define 20 |
216 | #define 22 |
217 | #define 24 |
218 | #define 26 |
219 | |
220 | int amdgpu_parse_extended_power_table(struct amdgpu_device *adev) |
221 | { |
222 | struct amdgpu_mode_info *mode_info = &adev->mode_info; |
223 | union power_info *power_info; |
224 | union fan_info *fan_info; |
225 | ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table; |
226 | int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); |
227 | u16 data_offset; |
228 | u8 frev, crev; |
229 | int ret, i; |
230 | |
231 | if (!amdgpu_atom_parse_data_header(ctx: mode_info->atom_context, index, NULL, |
232 | frev: &frev, crev: &crev, data_start: &data_offset)) |
233 | return -EINVAL; |
234 | power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); |
235 | |
236 | /* fan table */ |
237 | if (le16_to_cpu(power_info->pplib.usTableSize) >= |
238 | sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { |
239 | if (power_info->pplib3.usFanTableOffset) { |
240 | fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset + |
241 | le16_to_cpu(power_info->pplib3.usFanTableOffset)); |
242 | adev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst; |
243 | adev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin); |
244 | adev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed); |
245 | adev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh); |
246 | adev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin); |
247 | adev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed); |
248 | adev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh); |
249 | if (fan_info->fan.ucFanTableFormat >= 2) |
250 | adev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax); |
251 | else |
252 | adev->pm.dpm.fan.t_max = 10900; |
253 | adev->pm.dpm.fan.cycle_delay = 100000; |
254 | if (fan_info->fan.ucFanTableFormat >= 3) { |
255 | adev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode; |
256 | adev->pm.dpm.fan.default_max_fan_pwm = |
257 | le16_to_cpu(fan_info->fan3.usFanPWMMax); |
258 | adev->pm.dpm.fan.default_fan_output_sensitivity = 4836; |
259 | adev->pm.dpm.fan.fan_output_sensitivity = |
260 | le16_to_cpu(fan_info->fan3.usFanOutputSensitivity); |
261 | } |
262 | adev->pm.dpm.fan.ucode_fan_control = true; |
263 | } |
264 | } |
265 | |
266 | /* clock dependancy tables, shedding tables */ |
267 | if (le16_to_cpu(power_info->pplib.usTableSize) >= |
268 | sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) { |
269 | if (power_info->pplib4.usVddcDependencyOnSCLKOffset) { |
270 | dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) |
271 | (mode_info->atom_context->bios + data_offset + |
272 | le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset)); |
273 | ret = amdgpu_parse_clk_voltage_dep_table(amdgpu_table: &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk, |
274 | atom_table: dep_table); |
275 | if (ret) |
276 | return ret; |
277 | } |
278 | if (power_info->pplib4.usVddciDependencyOnMCLKOffset) { |
279 | dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) |
280 | (mode_info->atom_context->bios + data_offset + |
281 | le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset)); |
282 | ret = amdgpu_parse_clk_voltage_dep_table(amdgpu_table: &adev->pm.dpm.dyn_state.vddci_dependency_on_mclk, |
283 | atom_table: dep_table); |
284 | if (ret) |
285 | return ret; |
286 | } |
287 | if (power_info->pplib4.usVddcDependencyOnMCLKOffset) { |
288 | dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) |
289 | (mode_info->atom_context->bios + data_offset + |
290 | le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset)); |
291 | ret = amdgpu_parse_clk_voltage_dep_table(amdgpu_table: &adev->pm.dpm.dyn_state.vddc_dependency_on_mclk, |
292 | atom_table: dep_table); |
293 | if (ret) |
294 | return ret; |
295 | } |
296 | if (power_info->pplib4.usMvddDependencyOnMCLKOffset) { |
297 | dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) |
298 | (mode_info->atom_context->bios + data_offset + |
299 | le16_to_cpu(power_info->pplib4.usMvddDependencyOnMCLKOffset)); |
300 | ret = amdgpu_parse_clk_voltage_dep_table(amdgpu_table: &adev->pm.dpm.dyn_state.mvdd_dependency_on_mclk, |
301 | atom_table: dep_table); |
302 | if (ret) |
303 | return ret; |
304 | } |
305 | if (power_info->pplib4.usMaxClockVoltageOnDCOffset) { |
306 | ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v = |
307 | (ATOM_PPLIB_Clock_Voltage_Limit_Table *) |
308 | (mode_info->atom_context->bios + data_offset + |
309 | le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset)); |
310 | if (clk_v->ucNumEntries) { |
311 | adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk = |
312 | le16_to_cpu(clk_v->entries[0].usSclkLow) | |
313 | (clk_v->entries[0].ucSclkHigh << 16); |
314 | adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk = |
315 | le16_to_cpu(clk_v->entries[0].usMclkLow) | |
316 | (clk_v->entries[0].ucMclkHigh << 16); |
317 | adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc = |
318 | le16_to_cpu(clk_v->entries[0].usVddc); |
319 | adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci = |
320 | le16_to_cpu(clk_v->entries[0].usVddci); |
321 | } |
322 | } |
323 | if (power_info->pplib4.usVddcPhaseShedLimitsTableOffset) { |
324 | ATOM_PPLIB_PhaseSheddingLimits_Table *psl = |
325 | (ATOM_PPLIB_PhaseSheddingLimits_Table *) |
326 | (mode_info->atom_context->bios + data_offset + |
327 | le16_to_cpu(power_info->pplib4.usVddcPhaseShedLimitsTableOffset)); |
328 | ATOM_PPLIB_PhaseSheddingLimits_Record *entry; |
329 | |
330 | adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries = |
331 | kcalloc(n: psl->ucNumEntries, |
332 | size: sizeof(struct amdgpu_phase_shedding_limits_entry), |
333 | GFP_KERNEL); |
334 | if (!adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) |
335 | return -ENOMEM; |
336 | |
337 | entry = &psl->entries[0]; |
338 | for (i = 0; i < psl->ucNumEntries; i++) { |
339 | adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].sclk = |
340 | le16_to_cpu(entry->usSclkLow) | (entry->ucSclkHigh << 16); |
341 | adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].mclk = |
342 | le16_to_cpu(entry->usMclkLow) | (entry->ucMclkHigh << 16); |
343 | adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].voltage = |
344 | le16_to_cpu(entry->usVoltage); |
345 | entry = (ATOM_PPLIB_PhaseSheddingLimits_Record *) |
346 | ((u8 *)entry + sizeof(ATOM_PPLIB_PhaseSheddingLimits_Record)); |
347 | } |
348 | adev->pm.dpm.dyn_state.phase_shedding_limits_table.count = |
349 | psl->ucNumEntries; |
350 | } |
351 | } |
352 | |
353 | /* cac data */ |
354 | if (le16_to_cpu(power_info->pplib.usTableSize) >= |
355 | sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) { |
356 | adev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit); |
357 | adev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit); |
358 | adev->pm.dpm.near_tdp_limit_adjusted = adev->pm.dpm.near_tdp_limit; |
359 | adev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit); |
360 | if (adev->pm.dpm.tdp_od_limit) |
361 | adev->pm.dpm.power_control = true; |
362 | else |
363 | adev->pm.dpm.power_control = false; |
364 | adev->pm.dpm.tdp_adjustment = 0; |
365 | adev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold); |
366 | adev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage); |
367 | adev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope); |
368 | if (power_info->pplib5.usCACLeakageTableOffset) { |
369 | ATOM_PPLIB_CAC_Leakage_Table *cac_table = |
370 | (ATOM_PPLIB_CAC_Leakage_Table *) |
371 | (mode_info->atom_context->bios + data_offset + |
372 | le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset)); |
373 | ATOM_PPLIB_CAC_Leakage_Record *entry; |
374 | u32 size = cac_table->ucNumEntries * sizeof(struct amdgpu_cac_leakage_table); |
375 | adev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL); |
376 | if (!adev->pm.dpm.dyn_state.cac_leakage_table.entries) |
377 | return -ENOMEM; |
378 | entry = &cac_table->entries[0]; |
379 | for (i = 0; i < cac_table->ucNumEntries; i++) { |
380 | if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) { |
381 | adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1 = |
382 | le16_to_cpu(entry->usVddc1); |
383 | adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2 = |
384 | le16_to_cpu(entry->usVddc2); |
385 | adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3 = |
386 | le16_to_cpu(entry->usVddc3); |
387 | } else { |
388 | adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc = |
389 | le16_to_cpu(entry->usVddc); |
390 | adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage = |
391 | le32_to_cpu(entry->ulLeakageValue); |
392 | } |
393 | entry = (ATOM_PPLIB_CAC_Leakage_Record *) |
394 | ((u8 *)entry + sizeof(ATOM_PPLIB_CAC_Leakage_Record)); |
395 | } |
396 | adev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries; |
397 | } |
398 | } |
399 | |
400 | /* ext tables */ |
401 | if (le16_to_cpu(power_info->pplib.usTableSize) >= |
402 | sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { |
403 | ATOM_PPLIB_EXTENDEDHEADER *ext_hdr = (ATOM_PPLIB_EXTENDEDHEADER *) |
404 | (mode_info->atom_context->bios + data_offset + |
405 | le16_to_cpu(power_info->pplib3.usExtendendedHeaderOffset)); |
406 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) && |
407 | ext_hdr->usVCETableOffset) { |
408 | VCEClockInfoArray *array = (VCEClockInfoArray *) |
409 | (mode_info->atom_context->bios + data_offset + |
410 | le16_to_cpu(ext_hdr->usVCETableOffset) + 1); |
411 | ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *limits = |
412 | (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *) |
413 | (mode_info->atom_context->bios + data_offset + |
414 | le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + |
415 | 1 + array->ucNumEntries * sizeof(VCEClockInfo)); |
416 | ATOM_PPLIB_VCE_State_Table *states = |
417 | (ATOM_PPLIB_VCE_State_Table *) |
418 | (mode_info->atom_context->bios + data_offset + |
419 | le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + |
420 | 1 + (array->ucNumEntries * sizeof (VCEClockInfo)) + |
421 | 1 + (limits->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record))); |
422 | ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *entry; |
423 | ATOM_PPLIB_VCE_State_Record *state_entry; |
424 | VCEClockInfo *vce_clk; |
425 | u32 size = limits->numEntries * |
426 | sizeof(struct amdgpu_vce_clock_voltage_dependency_entry); |
427 | adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries = |
428 | kzalloc(size, GFP_KERNEL); |
429 | if (!adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries) |
430 | return -ENOMEM; |
431 | adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count = |
432 | limits->numEntries; |
433 | entry = &limits->entries[0]; |
434 | state_entry = &states->entries[0]; |
435 | for (i = 0; i < limits->numEntries; i++) { |
436 | vce_clk = (VCEClockInfo *) |
437 | ((u8 *)&array->entries[0] + |
438 | (entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); |
439 | adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].evclk = |
440 | le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); |
441 | adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].ecclk = |
442 | le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); |
443 | adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].v = |
444 | le16_to_cpu(entry->usVoltage); |
445 | entry = (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *) |
446 | ((u8 *)entry + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record)); |
447 | } |
448 | adev->pm.dpm.num_of_vce_states = |
449 | states->numEntries > AMD_MAX_VCE_LEVELS ? |
450 | AMD_MAX_VCE_LEVELS : states->numEntries; |
451 | for (i = 0; i < adev->pm.dpm.num_of_vce_states; i++) { |
452 | vce_clk = (VCEClockInfo *) |
453 | ((u8 *)&array->entries[0] + |
454 | (state_entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); |
455 | adev->pm.dpm.vce_states[i].evclk = |
456 | le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); |
457 | adev->pm.dpm.vce_states[i].ecclk = |
458 | le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); |
459 | adev->pm.dpm.vce_states[i].clk_idx = |
460 | state_entry->ucClockInfoIndex & 0x3f; |
461 | adev->pm.dpm.vce_states[i].pstate = |
462 | (state_entry->ucClockInfoIndex & 0xc0) >> 6; |
463 | state_entry = (ATOM_PPLIB_VCE_State_Record *) |
464 | ((u8 *)state_entry + sizeof(ATOM_PPLIB_VCE_State_Record)); |
465 | } |
466 | } |
467 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) && |
468 | ext_hdr->usUVDTableOffset) { |
469 | UVDClockInfoArray *array = (UVDClockInfoArray *) |
470 | (mode_info->atom_context->bios + data_offset + |
471 | le16_to_cpu(ext_hdr->usUVDTableOffset) + 1); |
472 | ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *limits = |
473 | (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *) |
474 | (mode_info->atom_context->bios + data_offset + |
475 | le16_to_cpu(ext_hdr->usUVDTableOffset) + 1 + |
476 | 1 + (array->ucNumEntries * sizeof (UVDClockInfo))); |
477 | ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *entry; |
478 | u32 size = limits->numEntries * |
479 | sizeof(struct amdgpu_uvd_clock_voltage_dependency_entry); |
480 | adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries = |
481 | kzalloc(size, GFP_KERNEL); |
482 | if (!adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries) |
483 | return -ENOMEM; |
484 | adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count = |
485 | limits->numEntries; |
486 | entry = &limits->entries[0]; |
487 | for (i = 0; i < limits->numEntries; i++) { |
488 | UVDClockInfo *uvd_clk = (UVDClockInfo *) |
489 | ((u8 *)&array->entries[0] + |
490 | (entry->ucUVDClockInfoIndex * sizeof(UVDClockInfo))); |
491 | adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].vclk = |
492 | le16_to_cpu(uvd_clk->usVClkLow) | (uvd_clk->ucVClkHigh << 16); |
493 | adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].dclk = |
494 | le16_to_cpu(uvd_clk->usDClkLow) | (uvd_clk->ucDClkHigh << 16); |
495 | adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].v = |
496 | le16_to_cpu(entry->usVoltage); |
497 | entry = (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *) |
498 | ((u8 *)entry + sizeof(ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record)); |
499 | } |
500 | } |
501 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) && |
502 | ext_hdr->usSAMUTableOffset) { |
503 | ATOM_PPLIB_SAMClk_Voltage_Limit_Table *limits = |
504 | (ATOM_PPLIB_SAMClk_Voltage_Limit_Table *) |
505 | (mode_info->atom_context->bios + data_offset + |
506 | le16_to_cpu(ext_hdr->usSAMUTableOffset) + 1); |
507 | ATOM_PPLIB_SAMClk_Voltage_Limit_Record *entry; |
508 | u32 size = limits->numEntries * |
509 | sizeof(struct amdgpu_clock_voltage_dependency_entry); |
510 | adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries = |
511 | kzalloc(size, GFP_KERNEL); |
512 | if (!adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries) |
513 | return -ENOMEM; |
514 | adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.count = |
515 | limits->numEntries; |
516 | entry = &limits->entries[0]; |
517 | for (i = 0; i < limits->numEntries; i++) { |
518 | adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].clk = |
519 | le16_to_cpu(entry->usSAMClockLow) | (entry->ucSAMClockHigh << 16); |
520 | adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].v = |
521 | le16_to_cpu(entry->usVoltage); |
522 | entry = (ATOM_PPLIB_SAMClk_Voltage_Limit_Record *) |
523 | ((u8 *)entry + sizeof(ATOM_PPLIB_SAMClk_Voltage_Limit_Record)); |
524 | } |
525 | } |
526 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) && |
527 | ext_hdr->usPPMTableOffset) { |
528 | ATOM_PPLIB_PPM_Table *ppm = (ATOM_PPLIB_PPM_Table *) |
529 | (mode_info->atom_context->bios + data_offset + |
530 | le16_to_cpu(ext_hdr->usPPMTableOffset)); |
531 | adev->pm.dpm.dyn_state.ppm_table = |
532 | kzalloc(size: sizeof(struct amdgpu_ppm_table), GFP_KERNEL); |
533 | if (!adev->pm.dpm.dyn_state.ppm_table) |
534 | return -ENOMEM; |
535 | adev->pm.dpm.dyn_state.ppm_table->ppm_design = ppm->ucPpmDesign; |
536 | adev->pm.dpm.dyn_state.ppm_table->cpu_core_number = |
537 | le16_to_cpu(ppm->usCpuCoreNumber); |
538 | adev->pm.dpm.dyn_state.ppm_table->platform_tdp = |
539 | le32_to_cpu(ppm->ulPlatformTDP); |
540 | adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdp = |
541 | le32_to_cpu(ppm->ulSmallACPlatformTDP); |
542 | adev->pm.dpm.dyn_state.ppm_table->platform_tdc = |
543 | le32_to_cpu(ppm->ulPlatformTDC); |
544 | adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdc = |
545 | le32_to_cpu(ppm->ulSmallACPlatformTDC); |
546 | adev->pm.dpm.dyn_state.ppm_table->apu_tdp = |
547 | le32_to_cpu(ppm->ulApuTDP); |
548 | adev->pm.dpm.dyn_state.ppm_table->dgpu_tdp = |
549 | le32_to_cpu(ppm->ulDGpuTDP); |
550 | adev->pm.dpm.dyn_state.ppm_table->dgpu_ulv_power = |
551 | le32_to_cpu(ppm->ulDGpuUlvPower); |
552 | adev->pm.dpm.dyn_state.ppm_table->tj_max = |
553 | le32_to_cpu(ppm->ulTjmax); |
554 | } |
555 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) && |
556 | ext_hdr->usACPTableOffset) { |
557 | ATOM_PPLIB_ACPClk_Voltage_Limit_Table *limits = |
558 | (ATOM_PPLIB_ACPClk_Voltage_Limit_Table *) |
559 | (mode_info->atom_context->bios + data_offset + |
560 | le16_to_cpu(ext_hdr->usACPTableOffset) + 1); |
561 | ATOM_PPLIB_ACPClk_Voltage_Limit_Record *entry; |
562 | u32 size = limits->numEntries * |
563 | sizeof(struct amdgpu_clock_voltage_dependency_entry); |
564 | adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries = |
565 | kzalloc(size, GFP_KERNEL); |
566 | if (!adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries) |
567 | return -ENOMEM; |
568 | adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.count = |
569 | limits->numEntries; |
570 | entry = &limits->entries[0]; |
571 | for (i = 0; i < limits->numEntries; i++) { |
572 | adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].clk = |
573 | le16_to_cpu(entry->usACPClockLow) | (entry->ucACPClockHigh << 16); |
574 | adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].v = |
575 | le16_to_cpu(entry->usVoltage); |
576 | entry = (ATOM_PPLIB_ACPClk_Voltage_Limit_Record *) |
577 | ((u8 *)entry + sizeof(ATOM_PPLIB_ACPClk_Voltage_Limit_Record)); |
578 | } |
579 | } |
580 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) && |
581 | ext_hdr->usPowerTuneTableOffset) { |
582 | u8 rev = *(u8 *)(mode_info->atom_context->bios + data_offset + |
583 | le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); |
584 | ATOM_PowerTune_Table *pt; |
585 | adev->pm.dpm.dyn_state.cac_tdp_table = |
586 | kzalloc(size: sizeof(struct amdgpu_cac_tdp_table), GFP_KERNEL); |
587 | if (!adev->pm.dpm.dyn_state.cac_tdp_table) |
588 | return -ENOMEM; |
589 | if (rev > 0) { |
590 | ATOM_PPLIB_POWERTUNE_Table_V1 *ppt = (ATOM_PPLIB_POWERTUNE_Table_V1 *) |
591 | (mode_info->atom_context->bios + data_offset + |
592 | le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); |
593 | adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = |
594 | ppt->usMaximumPowerDeliveryLimit; |
595 | pt = &ppt->power_tune_table; |
596 | } else { |
597 | ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *) |
598 | (mode_info->atom_context->bios + data_offset + |
599 | le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); |
600 | adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = 255; |
601 | pt = &ppt->power_tune_table; |
602 | } |
603 | adev->pm.dpm.dyn_state.cac_tdp_table->tdp = le16_to_cpu(pt->usTDP); |
604 | adev->pm.dpm.dyn_state.cac_tdp_table->configurable_tdp = |
605 | le16_to_cpu(pt->usConfigurableTDP); |
606 | adev->pm.dpm.dyn_state.cac_tdp_table->tdc = le16_to_cpu(pt->usTDC); |
607 | adev->pm.dpm.dyn_state.cac_tdp_table->battery_power_limit = |
608 | le16_to_cpu(pt->usBatteryPowerLimit); |
609 | adev->pm.dpm.dyn_state.cac_tdp_table->small_power_limit = |
610 | le16_to_cpu(pt->usSmallPowerLimit); |
611 | adev->pm.dpm.dyn_state.cac_tdp_table->low_cac_leakage = |
612 | le16_to_cpu(pt->usLowCACLeakage); |
613 | adev->pm.dpm.dyn_state.cac_tdp_table->high_cac_leakage = |
614 | le16_to_cpu(pt->usHighCACLeakage); |
615 | } |
616 | if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) && |
617 | ext_hdr->usSclkVddgfxTableOffset) { |
618 | dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) |
619 | (mode_info->atom_context->bios + data_offset + |
620 | le16_to_cpu(ext_hdr->usSclkVddgfxTableOffset)); |
621 | ret = amdgpu_parse_clk_voltage_dep_table( |
622 | amdgpu_table: &adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk, |
623 | atom_table: dep_table); |
624 | if (ret) |
625 | return ret; |
626 | } |
627 | } |
628 | |
629 | return 0; |
630 | } |
631 | |
632 | void amdgpu_free_extended_power_table(struct amdgpu_device *adev) |
633 | { |
634 | struct amdgpu_dpm_dynamic_state *dyn_state = &adev->pm.dpm.dyn_state; |
635 | |
636 | kfree(objp: dyn_state->vddc_dependency_on_sclk.entries); |
637 | kfree(objp: dyn_state->vddci_dependency_on_mclk.entries); |
638 | kfree(objp: dyn_state->vddc_dependency_on_mclk.entries); |
639 | kfree(objp: dyn_state->mvdd_dependency_on_mclk.entries); |
640 | kfree(objp: dyn_state->cac_leakage_table.entries); |
641 | kfree(objp: dyn_state->phase_shedding_limits_table.entries); |
642 | kfree(objp: dyn_state->ppm_table); |
643 | kfree(objp: dyn_state->cac_tdp_table); |
644 | kfree(objp: dyn_state->vce_clock_voltage_dependency_table.entries); |
645 | kfree(objp: dyn_state->uvd_clock_voltage_dependency_table.entries); |
646 | kfree(objp: dyn_state->samu_clock_voltage_dependency_table.entries); |
647 | kfree(objp: dyn_state->acp_clock_voltage_dependency_table.entries); |
648 | kfree(objp: dyn_state->vddgfx_dependency_on_sclk.entries); |
649 | } |
650 | |
651 | static const char *pp_lib_thermal_controller_names[] = { |
652 | "NONE" , |
653 | "lm63" , |
654 | "adm1032" , |
655 | "adm1030" , |
656 | "max6649" , |
657 | "lm64" , |
658 | "f75375" , |
659 | "RV6xx" , |
660 | "RV770" , |
661 | "adt7473" , |
662 | "NONE" , |
663 | "External GPIO" , |
664 | "Evergreen" , |
665 | "emc2103" , |
666 | "Sumo" , |
667 | "Northern Islands" , |
668 | "Southern Islands" , |
669 | "lm96163" , |
670 | "Sea Islands" , |
671 | "Kaveri/Kabini" , |
672 | }; |
673 | |
674 | void amdgpu_add_thermal_controller(struct amdgpu_device *adev) |
675 | { |
676 | struct amdgpu_mode_info *mode_info = &adev->mode_info; |
677 | ATOM_PPLIB_POWERPLAYTABLE *power_table; |
678 | int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); |
679 | ATOM_PPLIB_THERMALCONTROLLER *controller; |
680 | struct amdgpu_i2c_bus_rec i2c_bus; |
681 | u16 data_offset; |
682 | u8 frev, crev; |
683 | |
684 | if (!amdgpu_atom_parse_data_header(ctx: mode_info->atom_context, index, NULL, |
685 | frev: &frev, crev: &crev, data_start: &data_offset)) |
686 | return; |
687 | power_table = (ATOM_PPLIB_POWERPLAYTABLE *) |
688 | (mode_info->atom_context->bios + data_offset); |
689 | controller = &power_table->sThermalController; |
690 | |
691 | /* add the i2c bus for thermal/fan chip */ |
692 | if (controller->ucType > 0) { |
693 | if (controller->ucFanParameters & ATOM_PP_FANPARAMETERS_NOFAN) |
694 | adev->pm.no_fan = true; |
695 | adev->pm.fan_pulses_per_revolution = |
696 | controller->ucFanParameters & ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK; |
697 | if (adev->pm.fan_pulses_per_revolution) { |
698 | adev->pm.fan_min_rpm = controller->ucFanMinRPM; |
699 | adev->pm.fan_max_rpm = controller->ucFanMaxRPM; |
700 | } |
701 | if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) { |
702 | DRM_INFO("Internal thermal controller %s fan control\n" , |
703 | (controller->ucFanParameters & |
704 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
705 | adev->pm.int_thermal_type = THERMAL_TYPE_RV6XX; |
706 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) { |
707 | DRM_INFO("Internal thermal controller %s fan control\n" , |
708 | (controller->ucFanParameters & |
709 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
710 | adev->pm.int_thermal_type = THERMAL_TYPE_RV770; |
711 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN) { |
712 | DRM_INFO("Internal thermal controller %s fan control\n" , |
713 | (controller->ucFanParameters & |
714 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
715 | adev->pm.int_thermal_type = THERMAL_TYPE_EVERGREEN; |
716 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SUMO) { |
717 | DRM_INFO("Internal thermal controller %s fan control\n" , |
718 | (controller->ucFanParameters & |
719 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
720 | adev->pm.int_thermal_type = THERMAL_TYPE_SUMO; |
721 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_NISLANDS) { |
722 | DRM_INFO("Internal thermal controller %s fan control\n" , |
723 | (controller->ucFanParameters & |
724 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
725 | adev->pm.int_thermal_type = THERMAL_TYPE_NI; |
726 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SISLANDS) { |
727 | DRM_INFO("Internal thermal controller %s fan control\n" , |
728 | (controller->ucFanParameters & |
729 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
730 | adev->pm.int_thermal_type = THERMAL_TYPE_SI; |
731 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_CISLANDS) { |
732 | DRM_INFO("Internal thermal controller %s fan control\n" , |
733 | (controller->ucFanParameters & |
734 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
735 | adev->pm.int_thermal_type = THERMAL_TYPE_CI; |
736 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_KAVERI) { |
737 | DRM_INFO("Internal thermal controller %s fan control\n" , |
738 | (controller->ucFanParameters & |
739 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
740 | adev->pm.int_thermal_type = THERMAL_TYPE_KV; |
741 | } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) { |
742 | DRM_INFO("External GPIO thermal controller %s fan control\n" , |
743 | (controller->ucFanParameters & |
744 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
745 | adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL_GPIO; |
746 | } else if (controller->ucType == |
747 | ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) { |
748 | DRM_INFO("ADT7473 with internal thermal controller %s fan control\n" , |
749 | (controller->ucFanParameters & |
750 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
751 | adev->pm.int_thermal_type = THERMAL_TYPE_ADT7473_WITH_INTERNAL; |
752 | } else if (controller->ucType == |
753 | ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL) { |
754 | DRM_INFO("EMC2103 with internal thermal controller %s fan control\n" , |
755 | (controller->ucFanParameters & |
756 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
757 | adev->pm.int_thermal_type = THERMAL_TYPE_EMC2103_WITH_INTERNAL; |
758 | } else if (controller->ucType < ARRAY_SIZE(pp_lib_thermal_controller_names)) { |
759 | DRM_INFO("Possible %s thermal controller at 0x%02x %s fan control\n" , |
760 | pp_lib_thermal_controller_names[controller->ucType], |
761 | controller->ucI2cAddress >> 1, |
762 | (controller->ucFanParameters & |
763 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
764 | adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL; |
765 | i2c_bus = amdgpu_atombios_lookup_i2c_gpio(adev, id: controller->ucI2cLine); |
766 | adev->pm.i2c_bus = amdgpu_i2c_lookup(adev, i2c_bus: &i2c_bus); |
767 | if (adev->pm.i2c_bus) { |
768 | struct i2c_board_info info = { }; |
769 | const char *name = pp_lib_thermal_controller_names[controller->ucType]; |
770 | info.addr = controller->ucI2cAddress >> 1; |
771 | strscpy(info.type, name, sizeof(info.type)); |
772 | i2c_new_client_device(adap: &adev->pm.i2c_bus->adapter, info: &info); |
773 | } |
774 | } else { |
775 | DRM_INFO("Unknown thermal controller type %d at 0x%02x %s fan control\n" , |
776 | controller->ucType, |
777 | controller->ucI2cAddress >> 1, |
778 | (controller->ucFanParameters & |
779 | ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with" ); |
780 | } |
781 | } |
782 | } |
783 | |
784 | struct amd_vce_state* amdgpu_get_vce_clock_state(void *handle, u32 idx) |
785 | { |
786 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
787 | |
788 | if (idx < adev->pm.dpm.num_of_vce_states) |
789 | return &adev->pm.dpm.vce_states[idx]; |
790 | |
791 | return NULL; |
792 | } |
793 | |
794 | static struct amdgpu_ps *amdgpu_dpm_pick_power_state(struct amdgpu_device *adev, |
795 | enum amd_pm_state_type dpm_state) |
796 | { |
797 | int i; |
798 | struct amdgpu_ps *ps; |
799 | u32 ui_class; |
800 | bool single_display = (adev->pm.dpm.new_active_crtc_count < 2) ? |
801 | true : false; |
802 | |
803 | /* check if the vblank period is too short to adjust the mclk */ |
804 | if (single_display && adev->powerplay.pp_funcs->vblank_too_short) { |
805 | if (amdgpu_dpm_vblank_too_short(adev)) |
806 | single_display = false; |
807 | } |
808 | |
809 | /* certain older asics have a separare 3D performance state, |
810 | * so try that first if the user selected performance |
811 | */ |
812 | if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) |
813 | dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; |
814 | /* balanced states don't exist at the moment */ |
815 | if (dpm_state == POWER_STATE_TYPE_BALANCED) |
816 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; |
817 | |
818 | restart_search: |
819 | /* Pick the best power state based on current conditions */ |
820 | for (i = 0; i < adev->pm.dpm.num_ps; i++) { |
821 | ps = &adev->pm.dpm.ps[i]; |
822 | ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; |
823 | switch (dpm_state) { |
824 | /* user states */ |
825 | case POWER_STATE_TYPE_BATTERY: |
826 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { |
827 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { |
828 | if (single_display) |
829 | return ps; |
830 | } else |
831 | return ps; |
832 | } |
833 | break; |
834 | case POWER_STATE_TYPE_BALANCED: |
835 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { |
836 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { |
837 | if (single_display) |
838 | return ps; |
839 | } else |
840 | return ps; |
841 | } |
842 | break; |
843 | case POWER_STATE_TYPE_PERFORMANCE: |
844 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { |
845 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { |
846 | if (single_display) |
847 | return ps; |
848 | } else |
849 | return ps; |
850 | } |
851 | break; |
852 | /* internal states */ |
853 | case POWER_STATE_TYPE_INTERNAL_UVD: |
854 | if (adev->pm.dpm.uvd_ps) |
855 | return adev->pm.dpm.uvd_ps; |
856 | else |
857 | break; |
858 | case POWER_STATE_TYPE_INTERNAL_UVD_SD: |
859 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) |
860 | return ps; |
861 | break; |
862 | case POWER_STATE_TYPE_INTERNAL_UVD_HD: |
863 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) |
864 | return ps; |
865 | break; |
866 | case POWER_STATE_TYPE_INTERNAL_UVD_HD2: |
867 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) |
868 | return ps; |
869 | break; |
870 | case POWER_STATE_TYPE_INTERNAL_UVD_MVC: |
871 | if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) |
872 | return ps; |
873 | break; |
874 | case POWER_STATE_TYPE_INTERNAL_BOOT: |
875 | return adev->pm.dpm.boot_ps; |
876 | case POWER_STATE_TYPE_INTERNAL_THERMAL: |
877 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) |
878 | return ps; |
879 | break; |
880 | case POWER_STATE_TYPE_INTERNAL_ACPI: |
881 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) |
882 | return ps; |
883 | break; |
884 | case POWER_STATE_TYPE_INTERNAL_ULV: |
885 | if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) |
886 | return ps; |
887 | break; |
888 | case POWER_STATE_TYPE_INTERNAL_3DPERF: |
889 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) |
890 | return ps; |
891 | break; |
892 | default: |
893 | break; |
894 | } |
895 | } |
896 | /* use a fallback state if we didn't match */ |
897 | switch (dpm_state) { |
898 | case POWER_STATE_TYPE_INTERNAL_UVD_SD: |
899 | dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; |
900 | goto restart_search; |
901 | case POWER_STATE_TYPE_INTERNAL_UVD_HD: |
902 | case POWER_STATE_TYPE_INTERNAL_UVD_HD2: |
903 | case POWER_STATE_TYPE_INTERNAL_UVD_MVC: |
904 | if (adev->pm.dpm.uvd_ps) { |
905 | return adev->pm.dpm.uvd_ps; |
906 | } else { |
907 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; |
908 | goto restart_search; |
909 | } |
910 | case POWER_STATE_TYPE_INTERNAL_THERMAL: |
911 | dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; |
912 | goto restart_search; |
913 | case POWER_STATE_TYPE_INTERNAL_ACPI: |
914 | dpm_state = POWER_STATE_TYPE_BATTERY; |
915 | goto restart_search; |
916 | case POWER_STATE_TYPE_BATTERY: |
917 | case POWER_STATE_TYPE_BALANCED: |
918 | case POWER_STATE_TYPE_INTERNAL_3DPERF: |
919 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; |
920 | goto restart_search; |
921 | default: |
922 | break; |
923 | } |
924 | |
925 | return NULL; |
926 | } |
927 | |
928 | static int amdgpu_dpm_change_power_state_locked(struct amdgpu_device *adev) |
929 | { |
930 | const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; |
931 | struct amdgpu_ps *ps; |
932 | enum amd_pm_state_type dpm_state; |
933 | int ret; |
934 | bool equal = false; |
935 | |
936 | /* if dpm init failed */ |
937 | if (!adev->pm.dpm_enabled) |
938 | return 0; |
939 | |
940 | if (adev->pm.dpm.user_state != adev->pm.dpm.state) { |
941 | /* add other state override checks here */ |
942 | if ((!adev->pm.dpm.thermal_active) && |
943 | (!adev->pm.dpm.uvd_active)) |
944 | adev->pm.dpm.state = adev->pm.dpm.user_state; |
945 | } |
946 | dpm_state = adev->pm.dpm.state; |
947 | |
948 | ps = amdgpu_dpm_pick_power_state(adev, dpm_state); |
949 | if (ps) |
950 | adev->pm.dpm.requested_ps = ps; |
951 | else |
952 | return -EINVAL; |
953 | |
954 | if (amdgpu_dpm == 1 && pp_funcs->print_power_state) { |
955 | printk("switching from power state:\n" ); |
956 | amdgpu_dpm_print_power_state(adev, adev->pm.dpm.current_ps); |
957 | printk("switching to power state:\n" ); |
958 | amdgpu_dpm_print_power_state(adev, adev->pm.dpm.requested_ps); |
959 | } |
960 | |
961 | /* update whether vce is active */ |
962 | ps->vce_active = adev->pm.dpm.vce_active; |
963 | if (pp_funcs->display_configuration_changed) |
964 | amdgpu_dpm_display_configuration_changed(adev); |
965 | |
966 | ret = amdgpu_dpm_pre_set_power_state(adev); |
967 | if (ret) |
968 | return ret; |
969 | |
970 | if (pp_funcs->check_state_equal) { |
971 | if (0 != amdgpu_dpm_check_state_equal(adev, adev->pm.dpm.current_ps, adev->pm.dpm.requested_ps, &equal)) |
972 | equal = false; |
973 | } |
974 | |
975 | if (equal) |
976 | return 0; |
977 | |
978 | if (pp_funcs->set_power_state) |
979 | pp_funcs->set_power_state(adev->powerplay.pp_handle); |
980 | |
981 | amdgpu_dpm_post_set_power_state(adev); |
982 | |
983 | adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; |
984 | adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; |
985 | |
986 | if (pp_funcs->force_performance_level) { |
987 | if (adev->pm.dpm.thermal_active) { |
988 | enum amd_dpm_forced_level level = adev->pm.dpm.forced_level; |
989 | /* force low perf level for thermal */ |
990 | pp_funcs->force_performance_level(adev, AMD_DPM_FORCED_LEVEL_LOW); |
991 | /* save the user's level */ |
992 | adev->pm.dpm.forced_level = level; |
993 | } else { |
994 | /* otherwise, user selected level */ |
995 | pp_funcs->force_performance_level(adev, adev->pm.dpm.forced_level); |
996 | } |
997 | } |
998 | |
999 | return 0; |
1000 | } |
1001 | |
1002 | void amdgpu_legacy_dpm_compute_clocks(void *handle) |
1003 | { |
1004 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
1005 | |
1006 | amdgpu_dpm_get_active_displays(adev); |
1007 | |
1008 | amdgpu_dpm_change_power_state_locked(adev); |
1009 | } |
1010 | |
1011 | void amdgpu_dpm_thermal_work_handler(struct work_struct *work) |
1012 | { |
1013 | struct amdgpu_device *adev = |
1014 | container_of(work, struct amdgpu_device, |
1015 | pm.dpm.thermal.work); |
1016 | const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; |
1017 | /* switch to the thermal state */ |
1018 | enum amd_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; |
1019 | int temp, size = sizeof(temp); |
1020 | |
1021 | if (!adev->pm.dpm_enabled) |
1022 | return; |
1023 | |
1024 | if (!pp_funcs->read_sensor(adev->powerplay.pp_handle, |
1025 | AMDGPU_PP_SENSOR_GPU_TEMP, |
1026 | (void *)&temp, |
1027 | &size)) { |
1028 | if (temp < adev->pm.dpm.thermal.min_temp) |
1029 | /* switch back the user state */ |
1030 | dpm_state = adev->pm.dpm.user_state; |
1031 | } else { |
1032 | if (adev->pm.dpm.thermal.high_to_low) |
1033 | /* switch back the user state */ |
1034 | dpm_state = adev->pm.dpm.user_state; |
1035 | } |
1036 | |
1037 | if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) |
1038 | adev->pm.dpm.thermal_active = true; |
1039 | else |
1040 | adev->pm.dpm.thermal_active = false; |
1041 | |
1042 | adev->pm.dpm.state = dpm_state; |
1043 | |
1044 | amdgpu_legacy_dpm_compute_clocks(handle: adev->powerplay.pp_handle); |
1045 | } |
1046 | |