diff options
author | Alex Deucher <alexander.deucher@amd.com> | 2015-04-20 16:55:21 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2015-06-03 21:03:15 -0400 |
commit | d38ceaf99ed015f2a0b9af3499791bd3a3daae21 (patch) | |
tree | c8e237ea218e8ed8a5f64c1654fc01fe5d2239cb /drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | |
parent | 97b2e202fba05b87d720318a6500a337100dab4d (diff) |
drm/amdgpu: add core driver (v4)
This adds the non-asic specific core driver code.
v2: remove extra kconfig option
v3: implement minor fixes from Fengguang Wu
v4: fix cast in amdgpu_ucode.c
Acked-by: Christian König <christian.koenig@amd.com>
Acked-by: Jammy Zhou <Jammy.Zhou@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c new file mode 100644 index 000000000000..89782543f854 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | |||
@@ -0,0 +1,801 @@ | |||
1 | /* | ||
2 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
3 | * copy of this software and associated documentation files (the "Software"), | ||
4 | * to deal in the Software without restriction, including without limitation | ||
5 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
6 | * and/or sell copies of the Software, and to permit persons to whom the | ||
7 | * Software is furnished to do so, subject to the following conditions: | ||
8 | * | ||
9 | * The above copyright notice and this permission notice shall be included in | ||
10 | * all copies or substantial portions of the Software. | ||
11 | * | ||
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
13 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
15 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
16 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
17 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
18 | * OTHER DEALINGS IN THE SOFTWARE. | ||
19 | * | ||
20 | * Authors: Rafał Miłecki <zajec5@gmail.com> | ||
21 | * Alex Deucher <alexdeucher@gmail.com> | ||
22 | */ | ||
23 | #include <drm/drmP.h> | ||
24 | #include "amdgpu.h" | ||
25 | #include "amdgpu_drv.h" | ||
26 | #include "amdgpu_pm.h" | ||
27 | #include "amdgpu_dpm.h" | ||
28 | #include "atom.h" | ||
29 | #include <linux/power_supply.h> | ||
30 | #include <linux/hwmon.h> | ||
31 | #include <linux/hwmon-sysfs.h> | ||
32 | |||
33 | static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev); | ||
34 | |||
35 | void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev) | ||
36 | { | ||
37 | if (adev->pm.dpm_enabled) { | ||
38 | mutex_lock(&adev->pm.mutex); | ||
39 | if (power_supply_is_system_supplied() > 0) | ||
40 | adev->pm.dpm.ac_power = true; | ||
41 | else | ||
42 | adev->pm.dpm.ac_power = false; | ||
43 | if (adev->pm.funcs->enable_bapm) | ||
44 | amdgpu_dpm_enable_bapm(adev, adev->pm.dpm.ac_power); | ||
45 | mutex_unlock(&adev->pm.mutex); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static ssize_t amdgpu_get_dpm_state(struct device *dev, | ||
50 | struct device_attribute *attr, | ||
51 | char *buf) | ||
52 | { | ||
53 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
54 | struct amdgpu_device *adev = ddev->dev_private; | ||
55 | enum amdgpu_pm_state_type pm = adev->pm.dpm.user_state; | ||
56 | |||
57 | return snprintf(buf, PAGE_SIZE, "%s\n", | ||
58 | (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : | ||
59 | (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); | ||
60 | } | ||
61 | |||
62 | static ssize_t amdgpu_set_dpm_state(struct device *dev, | ||
63 | struct device_attribute *attr, | ||
64 | const char *buf, | ||
65 | size_t count) | ||
66 | { | ||
67 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
68 | struct amdgpu_device *adev = ddev->dev_private; | ||
69 | |||
70 | mutex_lock(&adev->pm.mutex); | ||
71 | if (strncmp("battery", buf, strlen("battery")) == 0) | ||
72 | adev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; | ||
73 | else if (strncmp("balanced", buf, strlen("balanced")) == 0) | ||
74 | adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; | ||
75 | else if (strncmp("performance", buf, strlen("performance")) == 0) | ||
76 | adev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; | ||
77 | else { | ||
78 | mutex_unlock(&adev->pm.mutex); | ||
79 | count = -EINVAL; | ||
80 | goto fail; | ||
81 | } | ||
82 | mutex_unlock(&adev->pm.mutex); | ||
83 | |||
84 | /* Can't set dpm state when the card is off */ | ||
85 | if (!(adev->flags & AMDGPU_IS_PX) || | ||
86 | (ddev->switch_power_state == DRM_SWITCH_POWER_ON)) | ||
87 | amdgpu_pm_compute_clocks(adev); | ||
88 | fail: | ||
89 | return count; | ||
90 | } | ||
91 | |||
92 | static ssize_t amdgpu_get_dpm_forced_performance_level(struct device *dev, | ||
93 | struct device_attribute *attr, | ||
94 | char *buf) | ||
95 | { | ||
96 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
97 | struct amdgpu_device *adev = ddev->dev_private; | ||
98 | enum amdgpu_dpm_forced_level level = adev->pm.dpm.forced_level; | ||
99 | |||
100 | return snprintf(buf, PAGE_SIZE, "%s\n", | ||
101 | (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) ? "auto" : | ||
102 | (level == AMDGPU_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); | ||
103 | } | ||
104 | |||
105 | static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, | ||
106 | struct device_attribute *attr, | ||
107 | const char *buf, | ||
108 | size_t count) | ||
109 | { | ||
110 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
111 | struct amdgpu_device *adev = ddev->dev_private; | ||
112 | enum amdgpu_dpm_forced_level level; | ||
113 | int ret = 0; | ||
114 | |||
115 | mutex_lock(&adev->pm.mutex); | ||
116 | if (strncmp("low", buf, strlen("low")) == 0) { | ||
117 | level = AMDGPU_DPM_FORCED_LEVEL_LOW; | ||
118 | } else if (strncmp("high", buf, strlen("high")) == 0) { | ||
119 | level = AMDGPU_DPM_FORCED_LEVEL_HIGH; | ||
120 | } else if (strncmp("auto", buf, strlen("auto")) == 0) { | ||
121 | level = AMDGPU_DPM_FORCED_LEVEL_AUTO; | ||
122 | } else { | ||
123 | count = -EINVAL; | ||
124 | goto fail; | ||
125 | } | ||
126 | if (adev->pm.funcs->force_performance_level) { | ||
127 | if (adev->pm.dpm.thermal_active) { | ||
128 | count = -EINVAL; | ||
129 | goto fail; | ||
130 | } | ||
131 | ret = amdgpu_dpm_force_performance_level(adev, level); | ||
132 | if (ret) | ||
133 | count = -EINVAL; | ||
134 | } | ||
135 | fail: | ||
136 | mutex_unlock(&adev->pm.mutex); | ||
137 | |||
138 | return count; | ||
139 | } | ||
140 | |||
141 | static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, amdgpu_get_dpm_state, amdgpu_set_dpm_state); | ||
142 | static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, | ||
143 | amdgpu_get_dpm_forced_performance_level, | ||
144 | amdgpu_set_dpm_forced_performance_level); | ||
145 | |||
146 | static ssize_t amdgpu_hwmon_show_temp(struct device *dev, | ||
147 | struct device_attribute *attr, | ||
148 | char *buf) | ||
149 | { | ||
150 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
151 | int temp; | ||
152 | |||
153 | if (adev->pm.funcs->get_temperature) | ||
154 | temp = amdgpu_dpm_get_temperature(adev); | ||
155 | else | ||
156 | temp = 0; | ||
157 | |||
158 | return snprintf(buf, PAGE_SIZE, "%d\n", temp); | ||
159 | } | ||
160 | |||
161 | static ssize_t amdgpu_hwmon_show_temp_thresh(struct device *dev, | ||
162 | struct device_attribute *attr, | ||
163 | char *buf) | ||
164 | { | ||
165 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
166 | int hyst = to_sensor_dev_attr(attr)->index; | ||
167 | int temp; | ||
168 | |||
169 | if (hyst) | ||
170 | temp = adev->pm.dpm.thermal.min_temp; | ||
171 | else | ||
172 | temp = adev->pm.dpm.thermal.max_temp; | ||
173 | |||
174 | return snprintf(buf, PAGE_SIZE, "%d\n", temp); | ||
175 | } | ||
176 | |||
177 | static ssize_t amdgpu_hwmon_get_pwm1_enable(struct device *dev, | ||
178 | struct device_attribute *attr, | ||
179 | char *buf) | ||
180 | { | ||
181 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
182 | u32 pwm_mode = 0; | ||
183 | |||
184 | if (adev->pm.funcs->get_fan_control_mode) | ||
185 | pwm_mode = amdgpu_dpm_get_fan_control_mode(adev); | ||
186 | |||
187 | /* never 0 (full-speed), fuse or smc-controlled always */ | ||
188 | return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2); | ||
189 | } | ||
190 | |||
191 | static ssize_t amdgpu_hwmon_set_pwm1_enable(struct device *dev, | ||
192 | struct device_attribute *attr, | ||
193 | const char *buf, | ||
194 | size_t count) | ||
195 | { | ||
196 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
197 | int err; | ||
198 | int value; | ||
199 | |||
200 | if(!adev->pm.funcs->set_fan_control_mode) | ||
201 | return -EINVAL; | ||
202 | |||
203 | err = kstrtoint(buf, 10, &value); | ||
204 | if (err) | ||
205 | return err; | ||
206 | |||
207 | switch (value) { | ||
208 | case 1: /* manual, percent-based */ | ||
209 | amdgpu_dpm_set_fan_control_mode(adev, FDO_PWM_MODE_STATIC); | ||
210 | break; | ||
211 | default: /* disable */ | ||
212 | amdgpu_dpm_set_fan_control_mode(adev, 0); | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | return count; | ||
217 | } | ||
218 | |||
219 | static ssize_t amdgpu_hwmon_get_pwm1_min(struct device *dev, | ||
220 | struct device_attribute *attr, | ||
221 | char *buf) | ||
222 | { | ||
223 | return sprintf(buf, "%i\n", 0); | ||
224 | } | ||
225 | |||
226 | static ssize_t amdgpu_hwmon_get_pwm1_max(struct device *dev, | ||
227 | struct device_attribute *attr, | ||
228 | char *buf) | ||
229 | { | ||
230 | return sprintf(buf, "%i\n", 255); | ||
231 | } | ||
232 | |||
233 | static ssize_t amdgpu_hwmon_set_pwm1(struct device *dev, | ||
234 | struct device_attribute *attr, | ||
235 | const char *buf, size_t count) | ||
236 | { | ||
237 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
238 | int err; | ||
239 | u32 value; | ||
240 | |||
241 | err = kstrtou32(buf, 10, &value); | ||
242 | if (err) | ||
243 | return err; | ||
244 | |||
245 | value = (value * 100) / 255; | ||
246 | |||
247 | err = amdgpu_dpm_set_fan_speed_percent(adev, value); | ||
248 | if (err) | ||
249 | return err; | ||
250 | |||
251 | return count; | ||
252 | } | ||
253 | |||
254 | static ssize_t amdgpu_hwmon_get_pwm1(struct device *dev, | ||
255 | struct device_attribute *attr, | ||
256 | char *buf) | ||
257 | { | ||
258 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
259 | int err; | ||
260 | u32 speed; | ||
261 | |||
262 | err = amdgpu_dpm_get_fan_speed_percent(adev, &speed); | ||
263 | if (err) | ||
264 | return err; | ||
265 | |||
266 | speed = (speed * 255) / 100; | ||
267 | |||
268 | return sprintf(buf, "%i\n", speed); | ||
269 | } | ||
270 | |||
271 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, amdgpu_hwmon_show_temp, NULL, 0); | ||
272 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, amdgpu_hwmon_show_temp_thresh, NULL, 0); | ||
273 | static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, amdgpu_hwmon_show_temp_thresh, NULL, 1); | ||
274 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, amdgpu_hwmon_get_pwm1, amdgpu_hwmon_set_pwm1, 0); | ||
275 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, amdgpu_hwmon_get_pwm1_enable, amdgpu_hwmon_set_pwm1_enable, 0); | ||
276 | static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, amdgpu_hwmon_get_pwm1_min, NULL, 0); | ||
277 | static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, amdgpu_hwmon_get_pwm1_max, NULL, 0); | ||
278 | |||
279 | static struct attribute *hwmon_attributes[] = { | ||
280 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
281 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | ||
282 | &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, | ||
283 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
284 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
285 | &sensor_dev_attr_pwm1_min.dev_attr.attr, | ||
286 | &sensor_dev_attr_pwm1_max.dev_attr.attr, | ||
287 | NULL | ||
288 | }; | ||
289 | |||
290 | static umode_t hwmon_attributes_visible(struct kobject *kobj, | ||
291 | struct attribute *attr, int index) | ||
292 | { | ||
293 | struct device *dev = container_of(kobj, struct device, kobj); | ||
294 | struct amdgpu_device *adev = dev_get_drvdata(dev); | ||
295 | umode_t effective_mode = attr->mode; | ||
296 | |||
297 | /* Skip limit attributes if DPM is not enabled */ | ||
298 | if (!adev->pm.dpm_enabled && | ||
299 | (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || | ||
300 | attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) | ||
301 | return 0; | ||
302 | |||
303 | /* Skip fan attributes if fan is not present */ | ||
304 | if (adev->pm.no_fan && | ||
305 | (attr == &sensor_dev_attr_pwm1.dev_attr.attr || | ||
306 | attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || | ||
307 | attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || | ||
308 | attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) | ||
309 | return 0; | ||
310 | |||
311 | /* mask fan attributes if we have no bindings for this asic to expose */ | ||
312 | if ((!adev->pm.funcs->get_fan_speed_percent && | ||
313 | attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */ | ||
314 | (!adev->pm.funcs->get_fan_control_mode && | ||
315 | attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */ | ||
316 | effective_mode &= ~S_IRUGO; | ||
317 | |||
318 | if ((!adev->pm.funcs->set_fan_speed_percent && | ||
319 | attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */ | ||
320 | (!adev->pm.funcs->set_fan_control_mode && | ||
321 | attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */ | ||
322 | effective_mode &= ~S_IWUSR; | ||
323 | |||
324 | /* hide max/min values if we can't both query and manage the fan */ | ||
325 | if ((!adev->pm.funcs->set_fan_speed_percent && | ||
326 | !adev->pm.funcs->get_fan_speed_percent) && | ||
327 | (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || | ||
328 | attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) | ||
329 | return 0; | ||
330 | |||
331 | return effective_mode; | ||
332 | } | ||
333 | |||
334 | static const struct attribute_group hwmon_attrgroup = { | ||
335 | .attrs = hwmon_attributes, | ||
336 | .is_visible = hwmon_attributes_visible, | ||
337 | }; | ||
338 | |||
339 | static const struct attribute_group *hwmon_groups[] = { | ||
340 | &hwmon_attrgroup, | ||
341 | NULL | ||
342 | }; | ||
343 | |||
344 | void amdgpu_dpm_thermal_work_handler(struct work_struct *work) | ||
345 | { | ||
346 | struct amdgpu_device *adev = | ||
347 | container_of(work, struct amdgpu_device, | ||
348 | pm.dpm.thermal.work); | ||
349 | /* switch to the thermal state */ | ||
350 | enum amdgpu_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; | ||
351 | |||
352 | if (!adev->pm.dpm_enabled) | ||
353 | return; | ||
354 | |||
355 | if (adev->pm.funcs->get_temperature) { | ||
356 | int temp = amdgpu_dpm_get_temperature(adev); | ||
357 | |||
358 | if (temp < adev->pm.dpm.thermal.min_temp) | ||
359 | /* switch back the user state */ | ||
360 | dpm_state = adev->pm.dpm.user_state; | ||
361 | } else { | ||
362 | if (adev->pm.dpm.thermal.high_to_low) | ||
363 | /* switch back the user state */ | ||
364 | dpm_state = adev->pm.dpm.user_state; | ||
365 | } | ||
366 | mutex_lock(&adev->pm.mutex); | ||
367 | if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) | ||
368 | adev->pm.dpm.thermal_active = true; | ||
369 | else | ||
370 | adev->pm.dpm.thermal_active = false; | ||
371 | adev->pm.dpm.state = dpm_state; | ||
372 | mutex_unlock(&adev->pm.mutex); | ||
373 | |||
374 | amdgpu_pm_compute_clocks(adev); | ||
375 | } | ||
376 | |||
377 | static struct amdgpu_ps *amdgpu_dpm_pick_power_state(struct amdgpu_device *adev, | ||
378 | enum amdgpu_pm_state_type dpm_state) | ||
379 | { | ||
380 | int i; | ||
381 | struct amdgpu_ps *ps; | ||
382 | u32 ui_class; | ||
383 | bool single_display = (adev->pm.dpm.new_active_crtc_count < 2) ? | ||
384 | true : false; | ||
385 | |||
386 | /* check if the vblank period is too short to adjust the mclk */ | ||
387 | if (single_display && adev->pm.funcs->vblank_too_short) { | ||
388 | if (amdgpu_dpm_vblank_too_short(adev)) | ||
389 | single_display = false; | ||
390 | } | ||
391 | |||
392 | /* certain older asics have a separare 3D performance state, | ||
393 | * so try that first if the user selected performance | ||
394 | */ | ||
395 | if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) | ||
396 | dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; | ||
397 | /* balanced states don't exist at the moment */ | ||
398 | if (dpm_state == POWER_STATE_TYPE_BALANCED) | ||
399 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; | ||
400 | |||
401 | restart_search: | ||
402 | /* Pick the best power state based on current conditions */ | ||
403 | for (i = 0; i < adev->pm.dpm.num_ps; i++) { | ||
404 | ps = &adev->pm.dpm.ps[i]; | ||
405 | ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; | ||
406 | switch (dpm_state) { | ||
407 | /* user states */ | ||
408 | case POWER_STATE_TYPE_BATTERY: | ||
409 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { | ||
410 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { | ||
411 | if (single_display) | ||
412 | return ps; | ||
413 | } else | ||
414 | return ps; | ||
415 | } | ||
416 | break; | ||
417 | case POWER_STATE_TYPE_BALANCED: | ||
418 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { | ||
419 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { | ||
420 | if (single_display) | ||
421 | return ps; | ||
422 | } else | ||
423 | return ps; | ||
424 | } | ||
425 | break; | ||
426 | case POWER_STATE_TYPE_PERFORMANCE: | ||
427 | if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { | ||
428 | if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { | ||
429 | if (single_display) | ||
430 | return ps; | ||
431 | } else | ||
432 | return ps; | ||
433 | } | ||
434 | break; | ||
435 | /* internal states */ | ||
436 | case POWER_STATE_TYPE_INTERNAL_UVD: | ||
437 | if (adev->pm.dpm.uvd_ps) | ||
438 | return adev->pm.dpm.uvd_ps; | ||
439 | else | ||
440 | break; | ||
441 | case POWER_STATE_TYPE_INTERNAL_UVD_SD: | ||
442 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) | ||
443 | return ps; | ||
444 | break; | ||
445 | case POWER_STATE_TYPE_INTERNAL_UVD_HD: | ||
446 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) | ||
447 | return ps; | ||
448 | break; | ||
449 | case POWER_STATE_TYPE_INTERNAL_UVD_HD2: | ||
450 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) | ||
451 | return ps; | ||
452 | break; | ||
453 | case POWER_STATE_TYPE_INTERNAL_UVD_MVC: | ||
454 | if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) | ||
455 | return ps; | ||
456 | break; | ||
457 | case POWER_STATE_TYPE_INTERNAL_BOOT: | ||
458 | return adev->pm.dpm.boot_ps; | ||
459 | case POWER_STATE_TYPE_INTERNAL_THERMAL: | ||
460 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) | ||
461 | return ps; | ||
462 | break; | ||
463 | case POWER_STATE_TYPE_INTERNAL_ACPI: | ||
464 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) | ||
465 | return ps; | ||
466 | break; | ||
467 | case POWER_STATE_TYPE_INTERNAL_ULV: | ||
468 | if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) | ||
469 | return ps; | ||
470 | break; | ||
471 | case POWER_STATE_TYPE_INTERNAL_3DPERF: | ||
472 | if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) | ||
473 | return ps; | ||
474 | break; | ||
475 | default: | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | /* use a fallback state if we didn't match */ | ||
480 | switch (dpm_state) { | ||
481 | case POWER_STATE_TYPE_INTERNAL_UVD_SD: | ||
482 | dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; | ||
483 | goto restart_search; | ||
484 | case POWER_STATE_TYPE_INTERNAL_UVD_HD: | ||
485 | case POWER_STATE_TYPE_INTERNAL_UVD_HD2: | ||
486 | case POWER_STATE_TYPE_INTERNAL_UVD_MVC: | ||
487 | if (adev->pm.dpm.uvd_ps) { | ||
488 | return adev->pm.dpm.uvd_ps; | ||
489 | } else { | ||
490 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; | ||
491 | goto restart_search; | ||
492 | } | ||
493 | case POWER_STATE_TYPE_INTERNAL_THERMAL: | ||
494 | dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; | ||
495 | goto restart_search; | ||
496 | case POWER_STATE_TYPE_INTERNAL_ACPI: | ||
497 | dpm_state = POWER_STATE_TYPE_BATTERY; | ||
498 | goto restart_search; | ||
499 | case POWER_STATE_TYPE_BATTERY: | ||
500 | case POWER_STATE_TYPE_BALANCED: | ||
501 | case POWER_STATE_TYPE_INTERNAL_3DPERF: | ||
502 | dpm_state = POWER_STATE_TYPE_PERFORMANCE; | ||
503 | goto restart_search; | ||
504 | default: | ||
505 | break; | ||
506 | } | ||
507 | |||
508 | return NULL; | ||
509 | } | ||
510 | |||
511 | static void amdgpu_dpm_change_power_state_locked(struct amdgpu_device *adev) | ||
512 | { | ||
513 | int i; | ||
514 | struct amdgpu_ps *ps; | ||
515 | enum amdgpu_pm_state_type dpm_state; | ||
516 | int ret; | ||
517 | |||
518 | /* if dpm init failed */ | ||
519 | if (!adev->pm.dpm_enabled) | ||
520 | return; | ||
521 | |||
522 | if (adev->pm.dpm.user_state != adev->pm.dpm.state) { | ||
523 | /* add other state override checks here */ | ||
524 | if ((!adev->pm.dpm.thermal_active) && | ||
525 | (!adev->pm.dpm.uvd_active)) | ||
526 | adev->pm.dpm.state = adev->pm.dpm.user_state; | ||
527 | } | ||
528 | dpm_state = adev->pm.dpm.state; | ||
529 | |||
530 | ps = amdgpu_dpm_pick_power_state(adev, dpm_state); | ||
531 | if (ps) | ||
532 | adev->pm.dpm.requested_ps = ps; | ||
533 | else | ||
534 | return; | ||
535 | |||
536 | /* no need to reprogram if nothing changed unless we are on BTC+ */ | ||
537 | if (adev->pm.dpm.current_ps == adev->pm.dpm.requested_ps) { | ||
538 | /* vce just modifies an existing state so force a change */ | ||
539 | if (ps->vce_active != adev->pm.dpm.vce_active) | ||
540 | goto force; | ||
541 | if (adev->flags & AMDGPU_IS_APU) { | ||
542 | /* for APUs if the num crtcs changed but state is the same, | ||
543 | * all we need to do is update the display configuration. | ||
544 | */ | ||
545 | if (adev->pm.dpm.new_active_crtcs != adev->pm.dpm.current_active_crtcs) { | ||
546 | /* update display watermarks based on new power state */ | ||
547 | amdgpu_display_bandwidth_update(adev); | ||
548 | /* update displays */ | ||
549 | amdgpu_dpm_display_configuration_changed(adev); | ||
550 | adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; | ||
551 | adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; | ||
552 | } | ||
553 | return; | ||
554 | } else { | ||
555 | /* for BTC+ if the num crtcs hasn't changed and state is the same, | ||
556 | * nothing to do, if the num crtcs is > 1 and state is the same, | ||
557 | * update display configuration. | ||
558 | */ | ||
559 | if (adev->pm.dpm.new_active_crtcs == | ||
560 | adev->pm.dpm.current_active_crtcs) { | ||
561 | return; | ||
562 | } else if ((adev->pm.dpm.current_active_crtc_count > 1) && | ||
563 | (adev->pm.dpm.new_active_crtc_count > 1)) { | ||
564 | /* update display watermarks based on new power state */ | ||
565 | amdgpu_display_bandwidth_update(adev); | ||
566 | /* update displays */ | ||
567 | amdgpu_dpm_display_configuration_changed(adev); | ||
568 | adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; | ||
569 | adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; | ||
570 | return; | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | |||
575 | force: | ||
576 | if (amdgpu_dpm == 1) { | ||
577 | printk("switching from power state:\n"); | ||
578 | amdgpu_dpm_print_power_state(adev, adev->pm.dpm.current_ps); | ||
579 | printk("switching to power state:\n"); | ||
580 | amdgpu_dpm_print_power_state(adev, adev->pm.dpm.requested_ps); | ||
581 | } | ||
582 | |||
583 | mutex_lock(&adev->ddev->struct_mutex); | ||
584 | down_write(&adev->pm.mclk_lock); | ||
585 | mutex_lock(&adev->ring_lock); | ||
586 | |||
587 | /* update whether vce is active */ | ||
588 | ps->vce_active = adev->pm.dpm.vce_active; | ||
589 | |||
590 | ret = amdgpu_dpm_pre_set_power_state(adev); | ||
591 | if (ret) | ||
592 | goto done; | ||
593 | |||
594 | /* update display watermarks based on new power state */ | ||
595 | amdgpu_display_bandwidth_update(adev); | ||
596 | /* update displays */ | ||
597 | amdgpu_dpm_display_configuration_changed(adev); | ||
598 | |||
599 | adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; | ||
600 | adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; | ||
601 | |||
602 | /* wait for the rings to drain */ | ||
603 | for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | ||
604 | struct amdgpu_ring *ring = adev->rings[i]; | ||
605 | if (ring && ring->ready) | ||
606 | amdgpu_fence_wait_empty(ring); | ||
607 | } | ||
608 | |||
609 | /* program the new power state */ | ||
610 | amdgpu_dpm_set_power_state(adev); | ||
611 | |||
612 | /* update current power state */ | ||
613 | adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps; | ||
614 | |||
615 | amdgpu_dpm_post_set_power_state(adev); | ||
616 | |||
617 | if (adev->pm.funcs->force_performance_level) { | ||
618 | if (adev->pm.dpm.thermal_active) { | ||
619 | enum amdgpu_dpm_forced_level level = adev->pm.dpm.forced_level; | ||
620 | /* force low perf level for thermal */ | ||
621 | amdgpu_dpm_force_performance_level(adev, AMDGPU_DPM_FORCED_LEVEL_LOW); | ||
622 | /* save the user's level */ | ||
623 | adev->pm.dpm.forced_level = level; | ||
624 | } else { | ||
625 | /* otherwise, user selected level */ | ||
626 | amdgpu_dpm_force_performance_level(adev, adev->pm.dpm.forced_level); | ||
627 | } | ||
628 | } | ||
629 | |||
630 | done: | ||
631 | mutex_unlock(&adev->ring_lock); | ||
632 | up_write(&adev->pm.mclk_lock); | ||
633 | mutex_unlock(&adev->ddev->struct_mutex); | ||
634 | } | ||
635 | |||
636 | void amdgpu_dpm_enable_uvd(struct amdgpu_device *adev, bool enable) | ||
637 | { | ||
638 | if (adev->pm.funcs->powergate_uvd) { | ||
639 | mutex_lock(&adev->pm.mutex); | ||
640 | /* enable/disable UVD */ | ||
641 | amdgpu_dpm_powergate_uvd(adev, !enable); | ||
642 | mutex_unlock(&adev->pm.mutex); | ||
643 | } else { | ||
644 | if (enable) { | ||
645 | mutex_lock(&adev->pm.mutex); | ||
646 | adev->pm.dpm.uvd_active = true; | ||
647 | adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD; | ||
648 | mutex_unlock(&adev->pm.mutex); | ||
649 | } else { | ||
650 | mutex_lock(&adev->pm.mutex); | ||
651 | adev->pm.dpm.uvd_active = false; | ||
652 | mutex_unlock(&adev->pm.mutex); | ||
653 | } | ||
654 | |||
655 | amdgpu_pm_compute_clocks(adev); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | void amdgpu_dpm_enable_vce(struct amdgpu_device *adev, bool enable) | ||
660 | { | ||
661 | if (enable) { | ||
662 | mutex_lock(&adev->pm.mutex); | ||
663 | adev->pm.dpm.vce_active = true; | ||
664 | /* XXX select vce level based on ring/task */ | ||
665 | adev->pm.dpm.vce_level = AMDGPU_VCE_LEVEL_AC_ALL; | ||
666 | mutex_unlock(&adev->pm.mutex); | ||
667 | } else { | ||
668 | mutex_lock(&adev->pm.mutex); | ||
669 | adev->pm.dpm.vce_active = false; | ||
670 | mutex_unlock(&adev->pm.mutex); | ||
671 | } | ||
672 | |||
673 | amdgpu_pm_compute_clocks(adev); | ||
674 | } | ||
675 | |||
676 | void amdgpu_pm_print_power_states(struct amdgpu_device *adev) | ||
677 | { | ||
678 | int i; | ||
679 | |||
680 | for (i = 0; i < adev->pm.dpm.num_ps; i++) { | ||
681 | printk("== power state %d ==\n", i); | ||
682 | amdgpu_dpm_print_power_state(adev, &adev->pm.dpm.ps[i]); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) | ||
687 | { | ||
688 | int ret; | ||
689 | |||
690 | if (adev->pm.funcs->get_temperature == NULL) | ||
691 | return 0; | ||
692 | adev->pm.int_hwmon_dev = hwmon_device_register_with_groups(adev->dev, | ||
693 | DRIVER_NAME, adev, | ||
694 | hwmon_groups); | ||
695 | if (IS_ERR(adev->pm.int_hwmon_dev)) { | ||
696 | ret = PTR_ERR(adev->pm.int_hwmon_dev); | ||
697 | dev_err(adev->dev, | ||
698 | "Unable to register hwmon device: %d\n", ret); | ||
699 | return ret; | ||
700 | } | ||
701 | |||
702 | ret = device_create_file(adev->dev, &dev_attr_power_dpm_state); | ||
703 | if (ret) { | ||
704 | DRM_ERROR("failed to create device file for dpm state\n"); | ||
705 | return ret; | ||
706 | } | ||
707 | ret = device_create_file(adev->dev, &dev_attr_power_dpm_force_performance_level); | ||
708 | if (ret) { | ||
709 | DRM_ERROR("failed to create device file for dpm state\n"); | ||
710 | return ret; | ||
711 | } | ||
712 | ret = amdgpu_debugfs_pm_init(adev); | ||
713 | if (ret) { | ||
714 | DRM_ERROR("Failed to register debugfs file for dpm!\n"); | ||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev) | ||
722 | { | ||
723 | if (adev->pm.int_hwmon_dev) | ||
724 | hwmon_device_unregister(adev->pm.int_hwmon_dev); | ||
725 | device_remove_file(adev->dev, &dev_attr_power_dpm_state); | ||
726 | device_remove_file(adev->dev, &dev_attr_power_dpm_force_performance_level); | ||
727 | } | ||
728 | |||
729 | void amdgpu_pm_compute_clocks(struct amdgpu_device *adev) | ||
730 | { | ||
731 | struct drm_device *ddev = adev->ddev; | ||
732 | struct drm_crtc *crtc; | ||
733 | struct amdgpu_crtc *amdgpu_crtc; | ||
734 | |||
735 | if (!adev->pm.dpm_enabled) | ||
736 | return; | ||
737 | |||
738 | mutex_lock(&adev->pm.mutex); | ||
739 | |||
740 | /* update active crtc counts */ | ||
741 | adev->pm.dpm.new_active_crtcs = 0; | ||
742 | adev->pm.dpm.new_active_crtc_count = 0; | ||
743 | if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { | ||
744 | list_for_each_entry(crtc, | ||
745 | &ddev->mode_config.crtc_list, head) { | ||
746 | amdgpu_crtc = to_amdgpu_crtc(crtc); | ||
747 | if (crtc->enabled) { | ||
748 | adev->pm.dpm.new_active_crtcs |= (1 << amdgpu_crtc->crtc_id); | ||
749 | adev->pm.dpm.new_active_crtc_count++; | ||
750 | } | ||
751 | } | ||
752 | } | ||
753 | |||
754 | /* update battery/ac status */ | ||
755 | if (power_supply_is_system_supplied() > 0) | ||
756 | adev->pm.dpm.ac_power = true; | ||
757 | else | ||
758 | adev->pm.dpm.ac_power = false; | ||
759 | |||
760 | amdgpu_dpm_change_power_state_locked(adev); | ||
761 | |||
762 | mutex_unlock(&adev->pm.mutex); | ||
763 | |||
764 | } | ||
765 | |||
766 | /* | ||
767 | * Debugfs info | ||
768 | */ | ||
769 | #if defined(CONFIG_DEBUG_FS) | ||
770 | |||
771 | static int amdgpu_debugfs_pm_info(struct seq_file *m, void *data) | ||
772 | { | ||
773 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
774 | struct drm_device *dev = node->minor->dev; | ||
775 | struct amdgpu_device *adev = dev->dev_private; | ||
776 | |||
777 | if (adev->pm.dpm_enabled) { | ||
778 | mutex_lock(&adev->pm.mutex); | ||
779 | if (adev->pm.funcs->debugfs_print_current_performance_level) | ||
780 | amdgpu_dpm_debugfs_print_current_performance_level(adev, m); | ||
781 | else | ||
782 | seq_printf(m, "Debugfs support not implemented for this asic\n"); | ||
783 | mutex_unlock(&adev->pm.mutex); | ||
784 | } | ||
785 | |||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | static struct drm_info_list amdgpu_pm_info_list[] = { | ||
790 | {"amdgpu_pm_info", amdgpu_debugfs_pm_info, 0, NULL}, | ||
791 | }; | ||
792 | #endif | ||
793 | |||
794 | static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev) | ||
795 | { | ||
796 | #if defined(CONFIG_DEBUG_FS) | ||
797 | return amdgpu_debugfs_add_files(adev, amdgpu_pm_info_list, ARRAY_SIZE(amdgpu_pm_info_list)); | ||
798 | #else | ||
799 | return 0; | ||
800 | #endif | ||
801 | } | ||