xref: /dflybsd-src/sys/dev/drm/radeon/radeon_pm.c (revision c59a5c484fdf34b9afa6e283014e4fff693253cc)
1926deccbSFrançois Tigeot /*
2926deccbSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
3926deccbSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
4926deccbSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
5926deccbSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6926deccbSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
7926deccbSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
8926deccbSFrançois Tigeot  *
9926deccbSFrançois Tigeot  * The above copyright notice and this permission notice shall be included in
10926deccbSFrançois Tigeot  * all copies or substantial portions of the Software.
11926deccbSFrançois Tigeot  *
12926deccbSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13926deccbSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14926deccbSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
15926deccbSFrançois Tigeot  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
16926deccbSFrançois Tigeot  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
17926deccbSFrançois Tigeot  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
18926deccbSFrançois Tigeot  * OTHER DEALINGS IN THE SOFTWARE.
19926deccbSFrançois Tigeot  *
20926deccbSFrançois Tigeot  * Authors: Rafał Miłecki <zajec5@gmail.com>
21926deccbSFrançois Tigeot  *          Alex Deucher <alexdeucher@gmail.com>
22926deccbSFrançois Tigeot  */
23926deccbSFrançois Tigeot #include <drm/drmP.h>
24926deccbSFrançois Tigeot #include "radeon.h"
25926deccbSFrançois Tigeot #include "avivod.h"
26926deccbSFrançois Tigeot #include "atom.h"
27*c59a5c48SFrançois Tigeot #include "r600_dpm.h"
281cfef1a5SFrançois Tigeot #include <linux/power_supply.h>
291cfef1a5SFrançois Tigeot #include <linux/hwmon.h>
301cfef1a5SFrançois Tigeot 
311cfef1a5SFrançois Tigeot #include <sys/power.h>
321cfef1a5SFrançois Tigeot #include <sys/sensors.h>
33926deccbSFrançois Tigeot 
34926deccbSFrançois Tigeot #define RADEON_IDLE_LOOP_MS 100
35926deccbSFrançois Tigeot #define RADEON_RECLOCK_DELAY_MS 200
36926deccbSFrançois Tigeot #define RADEON_WAIT_VBLANK_TIMEOUT 200
37926deccbSFrançois Tigeot 
38926deccbSFrançois Tigeot static const char *radeon_pm_state_type_name[5] = {
39926deccbSFrançois Tigeot 	"",
40926deccbSFrançois Tigeot 	"Powersave",
41926deccbSFrançois Tigeot 	"Battery",
42926deccbSFrançois Tigeot 	"Balanced",
43926deccbSFrançois Tigeot 	"Performance",
44926deccbSFrançois Tigeot };
45926deccbSFrançois Tigeot 
46926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work);
47926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev);
48926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev);
49926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
50926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev);
51926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev);
52926deccbSFrançois Tigeot 
53926deccbSFrançois Tigeot int radeon_pm_get_type_index(struct radeon_device *rdev,
54926deccbSFrançois Tigeot 			     enum radeon_pm_state_type ps_type,
55926deccbSFrançois Tigeot 			     int instance)
56926deccbSFrançois Tigeot {
57926deccbSFrançois Tigeot 	int i;
58926deccbSFrançois Tigeot 	int found_instance = -1;
59926deccbSFrançois Tigeot 
60926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
61926deccbSFrançois Tigeot 		if (rdev->pm.power_state[i].type == ps_type) {
62926deccbSFrançois Tigeot 			found_instance++;
63926deccbSFrançois Tigeot 			if (found_instance == instance)
64926deccbSFrançois Tigeot 				return i;
65926deccbSFrançois Tigeot 		}
66926deccbSFrançois Tigeot 	}
67926deccbSFrançois Tigeot 	/* return default if no match */
68926deccbSFrançois Tigeot 	return rdev->pm.default_power_state_index;
69926deccbSFrançois Tigeot }
70926deccbSFrançois Tigeot 
71926deccbSFrançois Tigeot void radeon_pm_acpi_event_handler(struct radeon_device *rdev)
72926deccbSFrançois Tigeot {
734cd92098Szrj 	if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
741cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
75c6f73aabSFrançois Tigeot 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
764cd92098Szrj 			rdev->pm.dpm.ac_power = true;
774cd92098Szrj 		else
784cd92098Szrj 			rdev->pm.dpm.ac_power = false;
79c6f73aabSFrançois Tigeot 		if (rdev->family == CHIP_ARUBA) {
804cd92098Szrj 			if (rdev->asic->dpm.enable_bapm)
814cd92098Szrj 				radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power);
82c6f73aabSFrançois Tigeot 		}
831cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
844cd92098Szrj         } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
85926deccbSFrançois Tigeot 		if (rdev->pm.profile == PM_PROFILE_AUTO) {
861cfef1a5SFrançois Tigeot 			mutex_lock(&rdev->pm.mutex);
87926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
88926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
891cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->pm.mutex);
90926deccbSFrançois Tigeot 		}
91926deccbSFrançois Tigeot 	}
92926deccbSFrançois Tigeot }
93926deccbSFrançois Tigeot 
94926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev)
95926deccbSFrançois Tigeot {
96926deccbSFrançois Tigeot 	switch (rdev->pm.profile) {
97926deccbSFrançois Tigeot 	case PM_PROFILE_DEFAULT:
98926deccbSFrançois Tigeot 		rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX;
99926deccbSFrançois Tigeot 		break;
100926deccbSFrançois Tigeot 	case PM_PROFILE_AUTO:
10157e252bfSMichael Neumann 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) {
102926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
103926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
104926deccbSFrançois Tigeot 			else
105926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
106926deccbSFrançois Tigeot 		} else {
107926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
108926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
109926deccbSFrançois Tigeot 			else
110926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
111926deccbSFrançois Tigeot 		}
112926deccbSFrançois Tigeot 		break;
113926deccbSFrançois Tigeot 	case PM_PROFILE_LOW:
114926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
115926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX;
116926deccbSFrançois Tigeot 		else
117926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX;
118926deccbSFrançois Tigeot 		break;
119926deccbSFrançois Tigeot 	case PM_PROFILE_MID:
120926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
121926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
122926deccbSFrançois Tigeot 		else
123926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
124926deccbSFrançois Tigeot 		break;
125926deccbSFrançois Tigeot 	case PM_PROFILE_HIGH:
126926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
127926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
128926deccbSFrançois Tigeot 		else
129926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
130926deccbSFrançois Tigeot 		break;
131926deccbSFrançois Tigeot 	}
132926deccbSFrançois Tigeot 
133926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count == 0) {
134926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
135926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx;
136926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
137926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx;
138926deccbSFrançois Tigeot 	} else {
139926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
140926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx;
141926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
142926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx;
143926deccbSFrançois Tigeot 	}
144926deccbSFrançois Tigeot }
145926deccbSFrançois Tigeot 
146926deccbSFrançois Tigeot static void radeon_unmap_vram_bos(struct radeon_device *rdev)
147926deccbSFrançois Tigeot {
148926deccbSFrançois Tigeot 	struct radeon_bo *bo, *n;
149926deccbSFrançois Tigeot 
150926deccbSFrançois Tigeot 	if (list_empty(&rdev->gem.objects))
151926deccbSFrançois Tigeot 		return;
152926deccbSFrançois Tigeot 
153926deccbSFrançois Tigeot 	list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
154926deccbSFrançois Tigeot 		if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
155926deccbSFrançois Tigeot 			ttm_bo_unmap_virtual(&bo->tbo);
156926deccbSFrançois Tigeot 	}
157926deccbSFrançois Tigeot }
158926deccbSFrançois Tigeot 
159926deccbSFrançois Tigeot static void radeon_sync_with_vblank(struct radeon_device *rdev)
160926deccbSFrançois Tigeot {
161926deccbSFrançois Tigeot 	if (rdev->pm.active_crtcs) {
162926deccbSFrançois Tigeot 		rdev->pm.vblank_sync = false;
163ee479021SImre Vadász #ifdef DUMBBELL_WIP
164926deccbSFrançois Tigeot 		wait_event_timeout(
165926deccbSFrançois Tigeot 			rdev->irq.vblank_queue, rdev->pm.vblank_sync,
166926deccbSFrançois Tigeot 			msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
167ee479021SImre Vadász #endif /* DUMBBELL_WIP */
168926deccbSFrançois Tigeot 	}
169926deccbSFrançois Tigeot }
170926deccbSFrançois Tigeot 
171926deccbSFrançois Tigeot static void radeon_set_power_state(struct radeon_device *rdev)
172926deccbSFrançois Tigeot {
173926deccbSFrançois Tigeot 	u32 sclk, mclk;
174926deccbSFrançois Tigeot 	bool misc_after = false;
175926deccbSFrançois Tigeot 
176926deccbSFrançois Tigeot 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
177926deccbSFrançois Tigeot 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
178926deccbSFrançois Tigeot 		return;
179926deccbSFrançois Tigeot 
180926deccbSFrançois Tigeot 	if (radeon_gui_idle(rdev)) {
181926deccbSFrançois Tigeot 		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
182926deccbSFrançois Tigeot 			clock_info[rdev->pm.requested_clock_mode_index].sclk;
183926deccbSFrançois Tigeot 		if (sclk > rdev->pm.default_sclk)
184926deccbSFrançois Tigeot 			sclk = rdev->pm.default_sclk;
185926deccbSFrançois Tigeot 
186926deccbSFrançois Tigeot 		/* starting with BTC, there is one state that is used for both
187926deccbSFrançois Tigeot 		 * MH and SH.  Difference is that we always use the high clock index for
188b403bed8SMichael Neumann 		 * mclk and vddci.
189926deccbSFrançois Tigeot 		 */
190926deccbSFrançois Tigeot 		if ((rdev->pm.pm_method == PM_METHOD_PROFILE) &&
191926deccbSFrançois Tigeot 		    (rdev->family >= CHIP_BARTS) &&
192926deccbSFrançois Tigeot 		    rdev->pm.active_crtc_count &&
193926deccbSFrançois Tigeot 		    ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) ||
194926deccbSFrançois Tigeot 		     (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX)))
195926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
196926deccbSFrançois Tigeot 				clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk;
197926deccbSFrançois Tigeot 		else
198926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
199926deccbSFrançois Tigeot 				clock_info[rdev->pm.requested_clock_mode_index].mclk;
200926deccbSFrançois Tigeot 
201926deccbSFrançois Tigeot 		if (mclk > rdev->pm.default_mclk)
202926deccbSFrançois Tigeot 			mclk = rdev->pm.default_mclk;
203926deccbSFrançois Tigeot 
204926deccbSFrançois Tigeot 		/* upvolt before raising clocks, downvolt after lowering clocks */
205926deccbSFrançois Tigeot 		if (sclk < rdev->pm.current_sclk)
206926deccbSFrançois Tigeot 			misc_after = true;
207926deccbSFrançois Tigeot 
208926deccbSFrançois Tigeot 		radeon_sync_with_vblank(rdev);
209926deccbSFrançois Tigeot 
210926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
211926deccbSFrançois Tigeot 			if (!radeon_pm_in_vbl(rdev))
212926deccbSFrançois Tigeot 				return;
213926deccbSFrançois Tigeot 		}
214926deccbSFrançois Tigeot 
215926deccbSFrançois Tigeot 		radeon_pm_prepare(rdev);
216926deccbSFrançois Tigeot 
217926deccbSFrançois Tigeot 		if (!misc_after)
218926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
219926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
220926deccbSFrançois Tigeot 
221926deccbSFrançois Tigeot 		/* set engine clock */
222926deccbSFrançois Tigeot 		if (sclk != rdev->pm.current_sclk) {
223926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
224926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, sclk);
225926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
226926deccbSFrançois Tigeot 			rdev->pm.current_sclk = sclk;
227926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk);
228926deccbSFrançois Tigeot 		}
229926deccbSFrançois Tigeot 
230926deccbSFrançois Tigeot 		/* set memory clock */
231926deccbSFrançois Tigeot 		if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) {
232926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
233926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, mclk);
234926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
235926deccbSFrançois Tigeot 			rdev->pm.current_mclk = mclk;
236926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk);
237926deccbSFrançois Tigeot 		}
238926deccbSFrançois Tigeot 
239926deccbSFrançois Tigeot 		if (misc_after)
240926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
241926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
242926deccbSFrançois Tigeot 
243926deccbSFrançois Tigeot 		radeon_pm_finish(rdev);
244926deccbSFrançois Tigeot 
245926deccbSFrançois Tigeot 		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
246926deccbSFrançois Tigeot 		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
247926deccbSFrançois Tigeot 	} else
248926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n");
249926deccbSFrançois Tigeot }
250926deccbSFrançois Tigeot 
251926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev)
252926deccbSFrançois Tigeot {
253926deccbSFrançois Tigeot 	int i, r;
254926deccbSFrançois Tigeot 
255926deccbSFrançois Tigeot 	/* no need to take locks, etc. if nothing's going to change */
256926deccbSFrançois Tigeot 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
257926deccbSFrançois Tigeot 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
258926deccbSFrançois Tigeot 		return;
259926deccbSFrançois Tigeot 
2601cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
2611cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
262926deccbSFrançois Tigeot 
263926deccbSFrançois Tigeot 	/* wait for the rings to drain */
264926deccbSFrançois Tigeot 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
265926deccbSFrançois Tigeot 		struct radeon_ring *ring = &rdev->ring[i];
266926deccbSFrançois Tigeot 		if (!ring->ready) {
267926deccbSFrançois Tigeot 			continue;
268926deccbSFrançois Tigeot 		}
269c6f73aabSFrançois Tigeot 		r = radeon_fence_wait_empty(rdev, i);
270926deccbSFrançois Tigeot 		if (r) {
271926deccbSFrançois Tigeot 			/* needs a GPU reset dont reset here */
2721cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->ring_lock);
2731cfef1a5SFrançois Tigeot 			up_write(&rdev->pm.mclk_lock);
274926deccbSFrançois Tigeot 			return;
275926deccbSFrançois Tigeot 		}
276926deccbSFrançois Tigeot 	}
277926deccbSFrançois Tigeot 
278926deccbSFrançois Tigeot 	radeon_unmap_vram_bos(rdev);
279926deccbSFrançois Tigeot 
280926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
281926deccbSFrançois Tigeot 		for (i = 0; i < rdev->num_crtc; i++) {
282926deccbSFrançois Tigeot 			if (rdev->pm.active_crtcs & (1 << i)) {
283926deccbSFrançois Tigeot 				rdev->pm.req_vblank |= (1 << i);
284ee479021SImre Vadász 				drm_vblank_get(rdev->ddev, i);
285926deccbSFrançois Tigeot 			}
286926deccbSFrançois Tigeot 		}
287926deccbSFrançois Tigeot 	}
288926deccbSFrançois Tigeot 
289926deccbSFrançois Tigeot 	radeon_set_power_state(rdev);
290926deccbSFrançois Tigeot 
291926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
292926deccbSFrançois Tigeot 		for (i = 0; i < rdev->num_crtc; i++) {
293926deccbSFrançois Tigeot 			if (rdev->pm.req_vblank & (1 << i)) {
294926deccbSFrançois Tigeot 				rdev->pm.req_vblank &= ~(1 << i);
295926deccbSFrançois Tigeot 				drm_vblank_put(rdev->ddev, i);
296926deccbSFrançois Tigeot 			}
297926deccbSFrançois Tigeot 		}
298926deccbSFrançois Tigeot 	}
299926deccbSFrançois Tigeot 
300926deccbSFrançois Tigeot 	/* update display watermarks based on new power state */
301926deccbSFrançois Tigeot 	radeon_update_bandwidth_info(rdev);
302926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count)
303926deccbSFrançois Tigeot 		radeon_bandwidth_update(rdev);
304926deccbSFrançois Tigeot 
305926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
306926deccbSFrançois Tigeot 
3071cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
3081cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
309926deccbSFrançois Tigeot }
310926deccbSFrançois Tigeot 
311926deccbSFrançois Tigeot static void radeon_pm_print_states(struct radeon_device *rdev)
312926deccbSFrançois Tigeot {
313926deccbSFrançois Tigeot 	int i, j;
314926deccbSFrançois Tigeot 	struct radeon_power_state *power_state;
315926deccbSFrançois Tigeot 	struct radeon_pm_clock_info *clock_info;
316926deccbSFrançois Tigeot 
317926deccbSFrançois Tigeot 	DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states);
318926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
319926deccbSFrançois Tigeot 		power_state = &rdev->pm.power_state[i];
320926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("State %d: %s\n", i,
321926deccbSFrançois Tigeot 			radeon_pm_state_type_name[power_state->type]);
322926deccbSFrançois Tigeot 		if (i == rdev->pm.default_power_state_index)
323926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tDefault");
324926deccbSFrançois Tigeot 		if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
325926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes);
326926deccbSFrançois Tigeot 		if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY)
327926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tSingle display only\n");
328926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes);
329926deccbSFrançois Tigeot 		for (j = 0; j < power_state->num_clock_modes; j++) {
330926deccbSFrançois Tigeot 			clock_info = &(power_state->clock_info[j]);
331926deccbSFrançois Tigeot 			if (rdev->flags & RADEON_IS_IGP)
332926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\n",
333926deccbSFrançois Tigeot 						 j,
334926deccbSFrançois Tigeot 						 clock_info->sclk * 10);
335926deccbSFrançois Tigeot 			else
336926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n",
337926deccbSFrançois Tigeot 						 j,
338926deccbSFrançois Tigeot 						 clock_info->sclk * 10,
339926deccbSFrançois Tigeot 						 clock_info->mclk * 10,
340926deccbSFrançois Tigeot 						 clock_info->voltage.voltage);
341926deccbSFrançois Tigeot 		}
342926deccbSFrançois Tigeot 	}
343926deccbSFrançois Tigeot }
344926deccbSFrançois Tigeot 
345926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
346926deccbSFrançois Tigeot static ssize_t radeon_get_pm_profile(struct device *dev,
347926deccbSFrançois Tigeot 				     struct device_attribute *attr,
348926deccbSFrançois Tigeot 				     char *buf)
349926deccbSFrançois Tigeot {
350c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
351926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
352926deccbSFrançois Tigeot 	int cp = rdev->pm.profile;
353926deccbSFrançois Tigeot 
354926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
355926deccbSFrançois Tigeot 			(cp == PM_PROFILE_AUTO) ? "auto" :
356926deccbSFrançois Tigeot 			(cp == PM_PROFILE_LOW) ? "low" :
357926deccbSFrançois Tigeot 			(cp == PM_PROFILE_MID) ? "mid" :
358926deccbSFrançois Tigeot 			(cp == PM_PROFILE_HIGH) ? "high" : "default");
359926deccbSFrançois Tigeot }
360926deccbSFrançois Tigeot 
361926deccbSFrançois Tigeot static ssize_t radeon_set_pm_profile(struct device *dev,
362926deccbSFrançois Tigeot 				     struct device_attribute *attr,
363926deccbSFrançois Tigeot 				     const char *buf,
364926deccbSFrançois Tigeot 				     size_t count)
365926deccbSFrançois Tigeot {
366c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
367926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
368926deccbSFrançois Tigeot 
369c6f73aabSFrançois Tigeot 	/* Can't set profile when the card is off */
370c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
371c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
372c6f73aabSFrançois Tigeot 		return -EINVAL;
373c6f73aabSFrançois Tigeot 
3741cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
375926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
376926deccbSFrançois Tigeot 		if (strncmp("default", buf, strlen("default")) == 0)
377926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
378926deccbSFrançois Tigeot 		else if (strncmp("auto", buf, strlen("auto")) == 0)
379926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_AUTO;
380926deccbSFrançois Tigeot 		else if (strncmp("low", buf, strlen("low")) == 0)
381926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_LOW;
382926deccbSFrançois Tigeot 		else if (strncmp("mid", buf, strlen("mid")) == 0)
383926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_MID;
384926deccbSFrançois Tigeot 		else if (strncmp("high", buf, strlen("high")) == 0)
385926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_HIGH;
386926deccbSFrançois Tigeot 		else {
387926deccbSFrançois Tigeot 			count = -EINVAL;
388926deccbSFrançois Tigeot 			goto fail;
389926deccbSFrançois Tigeot 		}
390926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
391926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
392926deccbSFrançois Tigeot 	} else
393926deccbSFrançois Tigeot 		count = -EINVAL;
394926deccbSFrançois Tigeot 
395926deccbSFrançois Tigeot fail:
3961cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
397926deccbSFrançois Tigeot 
398926deccbSFrançois Tigeot 	return count;
399926deccbSFrançois Tigeot }
400926deccbSFrançois Tigeot 
401926deccbSFrançois Tigeot static ssize_t radeon_get_pm_method(struct device *dev,
402926deccbSFrançois Tigeot 				    struct device_attribute *attr,
403926deccbSFrançois Tigeot 				    char *buf)
404926deccbSFrançois Tigeot {
405c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
406926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
407926deccbSFrançois Tigeot 	int pm = rdev->pm.pm_method;
408926deccbSFrançois Tigeot 
409926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
41057e252bfSMichael Neumann 			(pm == PM_METHOD_DYNPM) ? "dynpm" :
41157e252bfSMichael Neumann 			(pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
412926deccbSFrançois Tigeot }
413926deccbSFrançois Tigeot 
414926deccbSFrançois Tigeot static ssize_t radeon_set_pm_method(struct device *dev,
415926deccbSFrançois Tigeot 				    struct device_attribute *attr,
416926deccbSFrançois Tigeot 				    const char *buf,
417926deccbSFrançois Tigeot 				    size_t count)
418926deccbSFrançois Tigeot {
419c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
420926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
421926deccbSFrançois Tigeot 
422c6f73aabSFrançois Tigeot 	/* Can't set method when the card is off */
423c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
424c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
425c6f73aabSFrançois Tigeot 		count = -EINVAL;
426c6f73aabSFrançois Tigeot 		goto fail;
427c6f73aabSFrançois Tigeot 	}
428c6f73aabSFrançois Tigeot 
42957e252bfSMichael Neumann 	/* we don't support the legacy modes with dpm */
43057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
43157e252bfSMichael Neumann 		count = -EINVAL;
43257e252bfSMichael Neumann 		goto fail;
43357e252bfSMichael Neumann 	}
434926deccbSFrançois Tigeot 
435926deccbSFrançois Tigeot 	if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
4361cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
437926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_DYNPM;
438926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
439926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
4401cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
441926deccbSFrançois Tigeot 	} else if (strncmp("profile", buf, strlen("profile")) == 0) {
4421cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
443926deccbSFrançois Tigeot 		/* disable dynpm */
444926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
445926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
446926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_PROFILE;
4471cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
448ee479021SImre Vadász #ifdef DUMBBELL_WIP
449926deccbSFrançois Tigeot 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
450ee479021SImre Vadász #endif /* DUMBBELL_WIP */
451926deccbSFrançois Tigeot 	} else {
452926deccbSFrançois Tigeot 		count = -EINVAL;
453926deccbSFrançois Tigeot 		goto fail;
454926deccbSFrançois Tigeot 	}
455926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
456926deccbSFrançois Tigeot fail:
457926deccbSFrançois Tigeot 	return count;
458926deccbSFrançois Tigeot }
459926deccbSFrançois Tigeot 
46057e252bfSMichael Neumann static ssize_t radeon_get_dpm_state(struct device *dev,
46157e252bfSMichael Neumann 				    struct device_attribute *attr,
46257e252bfSMichael Neumann 				    char *buf)
46357e252bfSMichael Neumann {
464c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
46557e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
46657e252bfSMichael Neumann 	enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
46757e252bfSMichael Neumann 
46857e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
46957e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
47057e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
47157e252bfSMichael Neumann }
47257e252bfSMichael Neumann 
47357e252bfSMichael Neumann static ssize_t radeon_set_dpm_state(struct device *dev,
47457e252bfSMichael Neumann 				    struct device_attribute *attr,
47557e252bfSMichael Neumann 				    const char *buf,
47657e252bfSMichael Neumann 				    size_t count)
47757e252bfSMichael Neumann {
478c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
47957e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
48057e252bfSMichael Neumann 
4811cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
48257e252bfSMichael Neumann 	if (strncmp("battery", buf, strlen("battery")) == 0)
48357e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
48457e252bfSMichael Neumann 	else if (strncmp("balanced", buf, strlen("balanced")) == 0)
48557e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
48657e252bfSMichael Neumann 	else if (strncmp("performance", buf, strlen("performance")) == 0)
48757e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
48857e252bfSMichael Neumann 	else {
4891cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
49057e252bfSMichael Neumann 		count = -EINVAL;
49157e252bfSMichael Neumann 		goto fail;
49257e252bfSMichael Neumann 	}
4931cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
494c6f73aabSFrançois Tigeot 
495c6f73aabSFrançois Tigeot 	/* Can't set dpm state when the card is off */
496c6f73aabSFrançois Tigeot 	if (!(rdev->flags & RADEON_IS_PX) ||
497c6f73aabSFrançois Tigeot 	    (ddev->switch_power_state == DRM_SWITCH_POWER_ON))
49857e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
499c6f73aabSFrançois Tigeot 
50057e252bfSMichael Neumann fail:
50157e252bfSMichael Neumann 	return count;
50257e252bfSMichael Neumann }
50357e252bfSMichael Neumann 
50457e252bfSMichael Neumann static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
50557e252bfSMichael Neumann 						       struct device_attribute *attr,
50657e252bfSMichael Neumann 						       char *buf)
50757e252bfSMichael Neumann {
508c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
50957e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
51057e252bfSMichael Neumann 	enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
51157e252bfSMichael Neumann 
512c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
513c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
5141cfef1a5SFrançois Tigeot 		return ksnprintf(buf, PAGE_SIZE, "off\n");
515c6f73aabSFrançois Tigeot 
51657e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
51757e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
51857e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
51957e252bfSMichael Neumann }
52057e252bfSMichael Neumann 
52157e252bfSMichael Neumann static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
52257e252bfSMichael Neumann 						       struct device_attribute *attr,
52357e252bfSMichael Neumann 						       const char *buf,
52457e252bfSMichael Neumann 						       size_t count)
52557e252bfSMichael Neumann {
526c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
52757e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
52857e252bfSMichael Neumann 	enum radeon_dpm_forced_level level;
52957e252bfSMichael Neumann 	int ret = 0;
53057e252bfSMichael Neumann 
531c6f73aabSFrançois Tigeot 	/* Can't force performance level when the card is off */
532c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
533c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
534c6f73aabSFrançois Tigeot 		return -EINVAL;
535c6f73aabSFrançois Tigeot 
5361cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
53757e252bfSMichael Neumann 	if (strncmp("low", buf, strlen("low")) == 0) {
53857e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_LOW;
53957e252bfSMichael Neumann 	} else if (strncmp("high", buf, strlen("high")) == 0) {
54057e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_HIGH;
54157e252bfSMichael Neumann 	} else if (strncmp("auto", buf, strlen("auto")) == 0) {
54257e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_AUTO;
54357e252bfSMichael Neumann 	} else {
54457e252bfSMichael Neumann 		count = -EINVAL;
54557e252bfSMichael Neumann 		goto fail;
54657e252bfSMichael Neumann 	}
54757e252bfSMichael Neumann 	if (rdev->asic->dpm.force_performance_level) {
548c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
549c6f73aabSFrançois Tigeot 			count = -EINVAL;
550c6f73aabSFrançois Tigeot 			goto fail;
551c6f73aabSFrançois Tigeot 		}
55257e252bfSMichael Neumann 		ret = radeon_dpm_force_performance_level(rdev, level);
55357e252bfSMichael Neumann 		if (ret)
55457e252bfSMichael Neumann 			count = -EINVAL;
55557e252bfSMichael Neumann 	}
55657e252bfSMichael Neumann fail:
5571cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
558c6f73aabSFrançois Tigeot 
55957e252bfSMichael Neumann 	return count;
56057e252bfSMichael Neumann }
56157e252bfSMichael Neumann 
562*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev,
563*c59a5c48SFrançois Tigeot 					    struct device_attribute *attr,
564*c59a5c48SFrançois Tigeot 					    char *buf)
565*c59a5c48SFrançois Tigeot {
566*c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
567*c59a5c48SFrançois Tigeot 	u32 pwm_mode = 0;
568*c59a5c48SFrançois Tigeot 
569*c59a5c48SFrançois Tigeot 	if (rdev->asic->dpm.fan_ctrl_get_mode)
570*c59a5c48SFrançois Tigeot 		pwm_mode = rdev->asic->dpm.fan_ctrl_get_mode(rdev);
571*c59a5c48SFrançois Tigeot 
572*c59a5c48SFrançois Tigeot 	/* never 0 (full-speed), fuse or smc-controlled always */
573*c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2);
574*c59a5c48SFrançois Tigeot }
575*c59a5c48SFrançois Tigeot 
576*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev,
577*c59a5c48SFrançois Tigeot 					    struct device_attribute *attr,
578*c59a5c48SFrançois Tigeot 					    const char *buf,
579*c59a5c48SFrançois Tigeot 					    size_t count)
580*c59a5c48SFrançois Tigeot {
581*c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
582*c59a5c48SFrançois Tigeot 	int err;
583*c59a5c48SFrançois Tigeot 	int value;
584*c59a5c48SFrançois Tigeot 
585*c59a5c48SFrançois Tigeot 	if(!rdev->asic->dpm.fan_ctrl_set_mode)
586*c59a5c48SFrançois Tigeot 		return -EINVAL;
587*c59a5c48SFrançois Tigeot 
588*c59a5c48SFrançois Tigeot 	err = kstrtoint(buf, 10, &value);
589*c59a5c48SFrançois Tigeot 	if (err)
590*c59a5c48SFrançois Tigeot 		return err;
591*c59a5c48SFrançois Tigeot 
592*c59a5c48SFrançois Tigeot 	switch (value) {
593*c59a5c48SFrançois Tigeot 	case 1: /* manual, percent-based */
594*c59a5c48SFrançois Tigeot 		rdev->asic->dpm.fan_ctrl_set_mode(rdev, FDO_PWM_MODE_STATIC);
595*c59a5c48SFrançois Tigeot 		break;
596*c59a5c48SFrançois Tigeot 	default: /* disable */
597*c59a5c48SFrançois Tigeot 		rdev->asic->dpm.fan_ctrl_set_mode(rdev, 0);
598*c59a5c48SFrançois Tigeot 		break;
599*c59a5c48SFrançois Tigeot 	}
600*c59a5c48SFrançois Tigeot 
601*c59a5c48SFrançois Tigeot 	return count;
602*c59a5c48SFrançois Tigeot }
603*c59a5c48SFrançois Tigeot 
604*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_min(struct device *dev,
605*c59a5c48SFrançois Tigeot 					 struct device_attribute *attr,
606*c59a5c48SFrançois Tigeot 					 char *buf)
607*c59a5c48SFrançois Tigeot {
608*c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", 0);
609*c59a5c48SFrançois Tigeot }
610*c59a5c48SFrançois Tigeot 
611*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_max(struct device *dev,
612*c59a5c48SFrançois Tigeot 					 struct device_attribute *attr,
613*c59a5c48SFrançois Tigeot 					 char *buf)
614*c59a5c48SFrançois Tigeot {
615*c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", 255);
616*c59a5c48SFrançois Tigeot }
617*c59a5c48SFrançois Tigeot 
618*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_set_pwm1(struct device *dev,
619*c59a5c48SFrançois Tigeot 				     struct device_attribute *attr,
620*c59a5c48SFrançois Tigeot 				     const char *buf, size_t count)
621*c59a5c48SFrançois Tigeot {
622*c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
623*c59a5c48SFrançois Tigeot 	int err;
624*c59a5c48SFrançois Tigeot 	u32 value;
625*c59a5c48SFrançois Tigeot 
626*c59a5c48SFrançois Tigeot 	err = kstrtou32(buf, 10, &value);
627*c59a5c48SFrançois Tigeot 	if (err)
628*c59a5c48SFrançois Tigeot 		return err;
629*c59a5c48SFrançois Tigeot 
630*c59a5c48SFrançois Tigeot 	value = (value * 100) / 255;
631*c59a5c48SFrançois Tigeot 
632*c59a5c48SFrançois Tigeot 	err = rdev->asic->dpm.set_fan_speed_percent(rdev, value);
633*c59a5c48SFrançois Tigeot 	if (err)
634*c59a5c48SFrançois Tigeot 		return err;
635*c59a5c48SFrançois Tigeot 
636*c59a5c48SFrançois Tigeot 	return count;
637*c59a5c48SFrançois Tigeot }
638*c59a5c48SFrançois Tigeot 
639*c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1(struct device *dev,
640*c59a5c48SFrançois Tigeot 				     struct device_attribute *attr,
641*c59a5c48SFrançois Tigeot 				     char *buf)
642*c59a5c48SFrançois Tigeot {
643*c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
644*c59a5c48SFrançois Tigeot 	int err;
645*c59a5c48SFrançois Tigeot 	u32 speed;
646*c59a5c48SFrançois Tigeot 
647*c59a5c48SFrançois Tigeot 	err = rdev->asic->dpm.get_fan_speed_percent(rdev, &speed);
648*c59a5c48SFrançois Tigeot 	if (err)
649*c59a5c48SFrançois Tigeot 		return err;
650*c59a5c48SFrançois Tigeot 
651*c59a5c48SFrançois Tigeot 	speed = (speed * 255) / 100;
652*c59a5c48SFrançois Tigeot 
653*c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", speed);
654*c59a5c48SFrançois Tigeot }
655*c59a5c48SFrançois Tigeot 
656926deccbSFrançois Tigeot static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
657926deccbSFrançois Tigeot static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
65857e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
65957e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
66057e252bfSMichael Neumann 		   radeon_get_dpm_forced_performance_level,
66157e252bfSMichael Neumann 		   radeon_set_dpm_forced_performance_level);
662926deccbSFrançois Tigeot 
663926deccbSFrançois Tigeot static ssize_t radeon_hwmon_show_temp(struct device *dev,
664926deccbSFrançois Tigeot 				      struct device_attribute *attr,
665926deccbSFrançois Tigeot 				      char *buf)
666926deccbSFrançois Tigeot {
667c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
668c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
669926deccbSFrançois Tigeot 	int temp;
670926deccbSFrançois Tigeot 
671c6f73aabSFrançois Tigeot 	/* Can't get temperature when the card is off */
672c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
673c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
674c6f73aabSFrançois Tigeot 		return -EINVAL;
675c6f73aabSFrançois Tigeot 
67657e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature)
67757e252bfSMichael Neumann 		temp = radeon_get_temperature(rdev);
67857e252bfSMichael Neumann 	else
679926deccbSFrançois Tigeot 		temp = 0;
680926deccbSFrançois Tigeot 
681926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
682926deccbSFrançois Tigeot }
683926deccbSFrançois Tigeot 
684c6f73aabSFrançois Tigeot static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev,
685926deccbSFrançois Tigeot 					     struct device_attribute *attr,
686926deccbSFrançois Tigeot 					     char *buf)
687926deccbSFrançois Tigeot {
688c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
689c6f73aabSFrançois Tigeot 	int hyst = to_sensor_dev_attr(attr)->index;
690c6f73aabSFrançois Tigeot 	int temp;
691c6f73aabSFrançois Tigeot 
692c6f73aabSFrançois Tigeot 	if (hyst)
693c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.min_temp;
694c6f73aabSFrançois Tigeot 	else
695c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.max_temp;
696c6f73aabSFrançois Tigeot 
697c6f73aabSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
698926deccbSFrançois Tigeot }
699926deccbSFrançois Tigeot 
700926deccbSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0);
701c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0);
702c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1);
703*c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0);
704*c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0);
705*c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0);
706*c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0);
707*c59a5c48SFrançois Tigeot 
708926deccbSFrançois Tigeot 
709926deccbSFrançois Tigeot static struct attribute *hwmon_attributes[] = {
710926deccbSFrançois Tigeot 	&sensor_dev_attr_temp1_input.dev_attr.attr,
711c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
712c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
713*c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1.dev_attr.attr,
714*c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
715*c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_min.dev_attr.attr,
716*c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_max.dev_attr.attr,
717926deccbSFrançois Tigeot 	NULL
718926deccbSFrançois Tigeot };
719926deccbSFrançois Tigeot 
720c6f73aabSFrançois Tigeot static umode_t hwmon_attributes_visible(struct kobject *kobj,
721c6f73aabSFrançois Tigeot 					struct attribute *attr, int index)
722c6f73aabSFrançois Tigeot {
723c6f73aabSFrançois Tigeot 	struct device *dev = container_of(kobj, struct device, kobj);
724c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
725*c59a5c48SFrançois Tigeot 	umode_t effective_mode = attr->mode;
726c6f73aabSFrançois Tigeot 
727*c59a5c48SFrançois Tigeot 	/* Skip attributes if DPM is not enabled */
728c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method != PM_METHOD_DPM &&
729c6f73aabSFrançois Tigeot 	    (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr ||
730*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr ||
731*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr ||
732*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr ||
733*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
734*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
735c6f73aabSFrançois Tigeot 		return 0;
736c6f73aabSFrançois Tigeot 
737*c59a5c48SFrançois Tigeot 	/* Skip fan attributes if fan is not present */
738*c59a5c48SFrançois Tigeot 	if (rdev->pm.no_fan &&
739*c59a5c48SFrançois Tigeot 	    (attr == &sensor_dev_attr_pwm1.dev_attr.attr ||
740*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr ||
741*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
742*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
743*c59a5c48SFrançois Tigeot 		return 0;
744*c59a5c48SFrançois Tigeot 
745*c59a5c48SFrançois Tigeot 	/* mask fan attributes if we have no bindings for this asic to expose */
746*c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.get_fan_speed_percent &&
747*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */
748*c59a5c48SFrançois Tigeot 	    (!rdev->asic->dpm.fan_ctrl_get_mode &&
749*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */
750*c59a5c48SFrançois Tigeot 		effective_mode &= ~S_IRUGO;
751*c59a5c48SFrançois Tigeot 
752*c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.set_fan_speed_percent &&
753*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */
754*c59a5c48SFrançois Tigeot 	    (!rdev->asic->dpm.fan_ctrl_set_mode &&
755*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */
756*c59a5c48SFrançois Tigeot 		effective_mode &= ~S_IWUSR;
757*c59a5c48SFrançois Tigeot 
758*c59a5c48SFrançois Tigeot 	/* hide max/min values if we can't both query and manage the fan */
759*c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.set_fan_speed_percent &&
760*c59a5c48SFrançois Tigeot 	     !rdev->asic->dpm.get_fan_speed_percent) &&
761*c59a5c48SFrançois Tigeot 	    (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
762*c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
763*c59a5c48SFrançois Tigeot 		return 0;
764*c59a5c48SFrançois Tigeot 
765*c59a5c48SFrançois Tigeot 	return effective_mode;
766c6f73aabSFrançois Tigeot }
767c6f73aabSFrançois Tigeot 
768926deccbSFrançois Tigeot static const struct attribute_group hwmon_attrgroup = {
769926deccbSFrançois Tigeot 	.attrs = hwmon_attributes,
770c6f73aabSFrançois Tigeot 	.is_visible = hwmon_attributes_visible,
771c6f73aabSFrançois Tigeot };
772c6f73aabSFrançois Tigeot 
773c6f73aabSFrançois Tigeot static const struct attribute_group *hwmon_groups[] = {
774c6f73aabSFrançois Tigeot 	&hwmon_attrgroup,
775c6f73aabSFrançois Tigeot 	NULL
776926deccbSFrançois Tigeot };
777926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
778926deccbSFrançois Tigeot 
7792c86cb5bSImre Vadász static void
7802c86cb5bSImre Vadász radeon_hwmon_refresh(void *arg)
7812c86cb5bSImre Vadász {
7822c86cb5bSImre Vadász 	struct radeon_device *rdev = (struct radeon_device *)arg;
78326b5dbf2SImre Vadász 	struct drm_device *ddev = rdev->ddev;
7842c86cb5bSImre Vadász 	struct ksensor *s = rdev->pm.int_sensor;
7852c86cb5bSImre Vadász 	int temp;
78626b5dbf2SImre Vadász 	enum sensor_status stat;
7872c86cb5bSImre Vadász 
78826b5dbf2SImre Vadász 	/* Can't get temperature when the card is off */
78926b5dbf2SImre Vadász 	if  ((rdev->flags & RADEON_IS_PX) &&
79026b5dbf2SImre Vadász 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
79126b5dbf2SImre Vadász 		sensor_set_unknown(s);
79226b5dbf2SImre Vadász 		s->status = SENSOR_S_OK;
79326b5dbf2SImre Vadász 		return;
79426b5dbf2SImre Vadász 	}
79526b5dbf2SImre Vadász 
79626b5dbf2SImre Vadász 	if (rdev->asic->pm.get_temperature == NULL) {
79726b5dbf2SImre Vadász 		sensor_set_invalid(s);
79826b5dbf2SImre Vadász 		return;
79926b5dbf2SImre Vadász 	}
80026b5dbf2SImre Vadász 
8012c86cb5bSImre Vadász 	temp = radeon_get_temperature(rdev);
80226b5dbf2SImre Vadász 	if (temp >= rdev->pm.dpm.thermal.max_temp)
80326b5dbf2SImre Vadász 		stat = SENSOR_S_CRIT;
80426b5dbf2SImre Vadász 	else if (temp >= rdev->pm.dpm.thermal.min_temp)
80526b5dbf2SImre Vadász 		stat = SENSOR_S_WARN;
8062c86cb5bSImre Vadász 	else
80726b5dbf2SImre Vadász 		stat = SENSOR_S_OK;
8082c86cb5bSImre Vadász 
80926b5dbf2SImre Vadász 	sensor_set(s, temp * 1000 + 273150000, stat);
8102c86cb5bSImre Vadász }
8112c86cb5bSImre Vadász 
812926deccbSFrançois Tigeot static int radeon_hwmon_init(struct radeon_device *rdev)
813926deccbSFrançois Tigeot {
814926deccbSFrançois Tigeot 	int err = 0;
815926deccbSFrançois Tigeot 
8162c86cb5bSImre Vadász 	rdev->pm.int_sensor = NULL;
8172c86cb5bSImre Vadász 	rdev->pm.int_sensordev = NULL;
818926deccbSFrançois Tigeot 
819926deccbSFrançois Tigeot 	switch (rdev->pm.int_thermal_type) {
820926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV6XX:
821926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV770:
822926deccbSFrançois Tigeot 	case THERMAL_TYPE_EVERGREEN:
823926deccbSFrançois Tigeot 	case THERMAL_TYPE_NI:
824926deccbSFrançois Tigeot 	case THERMAL_TYPE_SUMO:
825926deccbSFrançois Tigeot 	case THERMAL_TYPE_SI:
8264cd92098Szrj 	case THERMAL_TYPE_CI:
8274cd92098Szrj 	case THERMAL_TYPE_KV:
82857e252bfSMichael Neumann 		if (rdev->asic->pm.get_temperature == NULL)
829926deccbSFrançois Tigeot 			return err;
8302c86cb5bSImre Vadász 
8312c86cb5bSImre Vadász 		rdev->pm.int_sensor = kmalloc(sizeof(*rdev->pm.int_sensor),
8322c86cb5bSImre Vadász 		    M_DRM, M_ZERO | M_WAITOK);
8332c86cb5bSImre Vadász 		rdev->pm.int_sensordev = kmalloc(
8342c86cb5bSImre Vadász 		    sizeof(*rdev->pm.int_sensordev), M_DRM,
8352c86cb5bSImre Vadász 		    M_ZERO | M_WAITOK);
8362c86cb5bSImre Vadász 		strlcpy(rdev->pm.int_sensordev->xname,
837fb572d17SFrançois Tigeot 		    device_get_nameunit(rdev->dev->bsddev),
8382c86cb5bSImre Vadász 		    sizeof(rdev->pm.int_sensordev->xname));
8392c86cb5bSImre Vadász 		rdev->pm.int_sensor->type = SENSOR_TEMP;
84026b5dbf2SImre Vadász 		rdev->pm.int_sensor->flags |= SENSOR_FINVALID;
8412c86cb5bSImre Vadász 		sensor_attach(rdev->pm.int_sensordev, rdev->pm.int_sensor);
8422c86cb5bSImre Vadász 		sensor_task_register(rdev, radeon_hwmon_refresh, 5);
8432c86cb5bSImre Vadász 		sensordev_install(rdev->pm.int_sensordev);
844926deccbSFrançois Tigeot 		break;
845926deccbSFrançois Tigeot 	default:
846926deccbSFrançois Tigeot 		break;
847926deccbSFrançois Tigeot 	}
848926deccbSFrançois Tigeot 
849926deccbSFrançois Tigeot 	return err;
850926deccbSFrançois Tigeot }
851926deccbSFrançois Tigeot 
852926deccbSFrançois Tigeot static void radeon_hwmon_fini(struct radeon_device *rdev)
853926deccbSFrançois Tigeot {
8542c86cb5bSImre Vadász 	if (rdev->pm.int_sensor != NULL && rdev->pm.int_sensordev != NULL) {
8552c86cb5bSImre Vadász 		sensordev_deinstall(rdev->pm.int_sensordev);
8562c86cb5bSImre Vadász 		sensor_task_unregister(rdev);
8572c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensor);
8582c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensordev);
8592c86cb5bSImre Vadász 		rdev->pm.int_sensor = NULL;
8602c86cb5bSImre Vadász 		rdev->pm.int_sensordev = NULL;
861926deccbSFrançois Tigeot 	}
862926deccbSFrançois Tigeot }
863926deccbSFrançois Tigeot 
8642c5cc6b9SFrançois Tigeot static void radeon_dpm_thermal_work_handler(struct work_struct *work)
86557e252bfSMichael Neumann {
8662c5cc6b9SFrançois Tigeot 	struct radeon_device *rdev =
8672c5cc6b9SFrançois Tigeot 		container_of(work, struct radeon_device,
8682c5cc6b9SFrançois Tigeot 			     pm.dpm.thermal.work);
86957e252bfSMichael Neumann 	/* switch to the thermal state */
87057e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
87157e252bfSMichael Neumann 
87257e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
87357e252bfSMichael Neumann 		return;
87457e252bfSMichael Neumann 
87557e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature) {
87657e252bfSMichael Neumann 		int temp = radeon_get_temperature(rdev);
87757e252bfSMichael Neumann 
87857e252bfSMichael Neumann 		if (temp < rdev->pm.dpm.thermal.min_temp)
87957e252bfSMichael Neumann 			/* switch back the user state */
88057e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
88157e252bfSMichael Neumann 	} else {
88257e252bfSMichael Neumann 		if (rdev->pm.dpm.thermal.high_to_low)
88357e252bfSMichael Neumann 			/* switch back the user state */
88457e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
88557e252bfSMichael Neumann 	}
8864cd92098Szrj 	mutex_lock(&rdev->pm.mutex);
8874cd92098Szrj 	if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL)
8884cd92098Szrj 		rdev->pm.dpm.thermal_active = true;
8894cd92098Szrj 	else
8904cd92098Szrj 		rdev->pm.dpm.thermal_active = false;
8914cd92098Szrj 	rdev->pm.dpm.state = dpm_state;
8924cd92098Szrj 	mutex_unlock(&rdev->pm.mutex);
8934cd92098Szrj 
8944cd92098Szrj 	radeon_pm_compute_clocks(rdev);
89557e252bfSMichael Neumann }
89657e252bfSMichael Neumann 
8977dcf36dcSFrançois Tigeot static bool radeon_dpm_single_display(struct radeon_device *rdev)
89857e252bfSMichael Neumann {
89957e252bfSMichael Neumann 	bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
90057e252bfSMichael Neumann 		true : false;
90157e252bfSMichael Neumann 
90257e252bfSMichael Neumann 	/* check if the vblank period is too short to adjust the mclk */
90357e252bfSMichael Neumann 	if (single_display && rdev->asic->dpm.vblank_too_short) {
90457e252bfSMichael Neumann 		if (radeon_dpm_vblank_too_short(rdev))
90557e252bfSMichael Neumann 			single_display = false;
90657e252bfSMichael Neumann 	}
90757e252bfSMichael Neumann 
9087dcf36dcSFrançois Tigeot 	return single_display;
9097dcf36dcSFrançois Tigeot }
9107dcf36dcSFrançois Tigeot 
9117dcf36dcSFrançois Tigeot static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
9127dcf36dcSFrançois Tigeot 						     enum radeon_pm_state_type dpm_state)
9137dcf36dcSFrançois Tigeot {
9147dcf36dcSFrançois Tigeot 	int i;
9157dcf36dcSFrançois Tigeot 	struct radeon_ps *ps;
9167dcf36dcSFrançois Tigeot 	u32 ui_class;
9177dcf36dcSFrançois Tigeot 	bool single_display = radeon_dpm_single_display(rdev);
9187dcf36dcSFrançois Tigeot 
919*c59a5c48SFrançois Tigeot 	/* 120hz tends to be problematic even if they are under the
920*c59a5c48SFrançois Tigeot 	 * vblank limit.
921*c59a5c48SFrançois Tigeot 	 */
922*c59a5c48SFrançois Tigeot 	if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120))
923*c59a5c48SFrançois Tigeot 		single_display = false;
924*c59a5c48SFrançois Tigeot 
92557e252bfSMichael Neumann 	/* certain older asics have a separare 3D performance state,
92657e252bfSMichael Neumann 	 * so try that first if the user selected performance
92757e252bfSMichael Neumann 	 */
92857e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
92957e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
93057e252bfSMichael Neumann 	/* balanced states don't exist at the moment */
93157e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_BALANCED)
93257e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
93357e252bfSMichael Neumann 
93457e252bfSMichael Neumann restart_search:
93557e252bfSMichael Neumann 	/* Pick the best power state based on current conditions */
93657e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
93757e252bfSMichael Neumann 		ps = &rdev->pm.dpm.ps[i];
93857e252bfSMichael Neumann 		ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
93957e252bfSMichael Neumann 		switch (dpm_state) {
94057e252bfSMichael Neumann 		/* user states */
94157e252bfSMichael Neumann 		case POWER_STATE_TYPE_BATTERY:
94257e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
94357e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
94457e252bfSMichael Neumann 					if (single_display)
94557e252bfSMichael Neumann 						return ps;
94657e252bfSMichael Neumann 				} else
94757e252bfSMichael Neumann 					return ps;
94857e252bfSMichael Neumann 			}
94957e252bfSMichael Neumann 			break;
95057e252bfSMichael Neumann 		case POWER_STATE_TYPE_BALANCED:
95157e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
95257e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
95357e252bfSMichael Neumann 					if (single_display)
95457e252bfSMichael Neumann 						return ps;
95557e252bfSMichael Neumann 				} else
95657e252bfSMichael Neumann 					return ps;
95757e252bfSMichael Neumann 			}
95857e252bfSMichael Neumann 			break;
95957e252bfSMichael Neumann 		case POWER_STATE_TYPE_PERFORMANCE:
96057e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
96157e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
96257e252bfSMichael Neumann 					if (single_display)
96357e252bfSMichael Neumann 						return ps;
96457e252bfSMichael Neumann 				} else
96557e252bfSMichael Neumann 					return ps;
96657e252bfSMichael Neumann 			}
96757e252bfSMichael Neumann 			break;
96857e252bfSMichael Neumann 		/* internal states */
96957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD:
9704cd92098Szrj 			if (rdev->pm.dpm.uvd_ps)
97157e252bfSMichael Neumann 				return rdev->pm.dpm.uvd_ps;
9724cd92098Szrj 			else
9734cd92098Szrj 				break;
97457e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_SD:
97557e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
97657e252bfSMichael Neumann 				return ps;
97757e252bfSMichael Neumann 			break;
97857e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD:
97957e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
98057e252bfSMichael Neumann 				return ps;
98157e252bfSMichael Neumann 			break;
98257e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
98357e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
98457e252bfSMichael Neumann 				return ps;
98557e252bfSMichael Neumann 			break;
98657e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
98757e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
98857e252bfSMichael Neumann 				return ps;
98957e252bfSMichael Neumann 			break;
99057e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_BOOT:
99157e252bfSMichael Neumann 			return rdev->pm.dpm.boot_ps;
99257e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_THERMAL:
99357e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
99457e252bfSMichael Neumann 				return ps;
99557e252bfSMichael Neumann 			break;
99657e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ACPI:
99757e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
99857e252bfSMichael Neumann 				return ps;
99957e252bfSMichael Neumann 			break;
100057e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ULV:
100157e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
100257e252bfSMichael Neumann 				return ps;
100357e252bfSMichael Neumann 			break;
100457e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_3DPERF:
100557e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
100657e252bfSMichael Neumann 				return ps;
100757e252bfSMichael Neumann 			break;
100857e252bfSMichael Neumann 		default:
100957e252bfSMichael Neumann 			break;
101057e252bfSMichael Neumann 		}
101157e252bfSMichael Neumann 	}
101257e252bfSMichael Neumann 	/* use a fallback state if we didn't match */
101357e252bfSMichael Neumann 	switch (dpm_state) {
101457e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_SD:
10154cd92098Szrj 		dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10164cd92098Szrj 		goto restart_search;
101757e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD:
101857e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
101957e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
10204cd92098Szrj 		if (rdev->pm.dpm.uvd_ps) {
102157e252bfSMichael Neumann 			return rdev->pm.dpm.uvd_ps;
10224cd92098Szrj 		} else {
10234cd92098Szrj 			dpm_state = POWER_STATE_TYPE_PERFORMANCE;
10244cd92098Szrj 			goto restart_search;
10254cd92098Szrj 		}
102657e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_THERMAL:
102757e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
102857e252bfSMichael Neumann 		goto restart_search;
102957e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_ACPI:
103057e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_BATTERY;
103157e252bfSMichael Neumann 		goto restart_search;
103257e252bfSMichael Neumann 	case POWER_STATE_TYPE_BATTERY:
103357e252bfSMichael Neumann 	case POWER_STATE_TYPE_BALANCED:
103457e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_3DPERF:
103557e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
103657e252bfSMichael Neumann 		goto restart_search;
103757e252bfSMichael Neumann 	default:
103857e252bfSMichael Neumann 		break;
103957e252bfSMichael Neumann 	}
104057e252bfSMichael Neumann 
104157e252bfSMichael Neumann 	return NULL;
104257e252bfSMichael Neumann }
104357e252bfSMichael Neumann 
104457e252bfSMichael Neumann static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
104557e252bfSMichael Neumann {
104657e252bfSMichael Neumann 	int i;
104757e252bfSMichael Neumann 	struct radeon_ps *ps;
104857e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state;
104957e252bfSMichael Neumann 	int ret;
10507dcf36dcSFrançois Tigeot 	bool single_display = radeon_dpm_single_display(rdev);
105157e252bfSMichael Neumann 
105257e252bfSMichael Neumann 	/* if dpm init failed */
105357e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
105457e252bfSMichael Neumann 		return;
105557e252bfSMichael Neumann 
105657e252bfSMichael Neumann 	if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
105757e252bfSMichael Neumann 		/* add other state override checks here */
105857e252bfSMichael Neumann 		if ((!rdev->pm.dpm.thermal_active) &&
105957e252bfSMichael Neumann 		    (!rdev->pm.dpm.uvd_active))
106057e252bfSMichael Neumann 			rdev->pm.dpm.state = rdev->pm.dpm.user_state;
106157e252bfSMichael Neumann 	}
106257e252bfSMichael Neumann 	dpm_state = rdev->pm.dpm.state;
106357e252bfSMichael Neumann 
106457e252bfSMichael Neumann 	ps = radeon_dpm_pick_power_state(rdev, dpm_state);
106557e252bfSMichael Neumann 	if (ps)
106657e252bfSMichael Neumann 		rdev->pm.dpm.requested_ps = ps;
106757e252bfSMichael Neumann 	else
106857e252bfSMichael Neumann 		return;
106957e252bfSMichael Neumann 
107057e252bfSMichael Neumann 	/* no need to reprogram if nothing changed unless we are on BTC+ */
107157e252bfSMichael Neumann 	if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
1072c6f73aabSFrançois Tigeot 		/* vce just modifies an existing state so force a change */
1073c6f73aabSFrançois Tigeot 		if (ps->vce_active != rdev->pm.dpm.vce_active)
1074c6f73aabSFrançois Tigeot 			goto force;
10757dcf36dcSFrançois Tigeot 		/* user has made a display change (such as timing) */
10767dcf36dcSFrançois Tigeot 		if (rdev->pm.dpm.single_display != single_display)
10777dcf36dcSFrançois Tigeot 			goto force;
107857e252bfSMichael Neumann 		if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
107957e252bfSMichael Neumann 			/* for pre-BTC and APUs if the num crtcs changed but state is the same,
108057e252bfSMichael Neumann 			 * all we need to do is update the display configuration.
108157e252bfSMichael Neumann 			 */
108257e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
108357e252bfSMichael Neumann 				/* update display watermarks based on new power state */
108457e252bfSMichael Neumann 				radeon_bandwidth_update(rdev);
108557e252bfSMichael Neumann 				/* update displays */
108657e252bfSMichael Neumann 				radeon_dpm_display_configuration_changed(rdev);
108757e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
108857e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
108957e252bfSMichael Neumann 			}
109057e252bfSMichael Neumann 			return;
109157e252bfSMichael Neumann 		} else {
109257e252bfSMichael Neumann 			/* for BTC+ if the num crtcs hasn't changed and state is the same,
109357e252bfSMichael Neumann 			 * nothing to do, if the num crtcs is > 1 and state is the same,
109457e252bfSMichael Neumann 			 * update display configuration.
109557e252bfSMichael Neumann 			 */
109657e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs ==
109757e252bfSMichael Neumann 			    rdev->pm.dpm.current_active_crtcs) {
109857e252bfSMichael Neumann 				return;
109957e252bfSMichael Neumann 			} else {
110057e252bfSMichael Neumann 				if ((rdev->pm.dpm.current_active_crtc_count > 1) &&
110157e252bfSMichael Neumann 				    (rdev->pm.dpm.new_active_crtc_count > 1)) {
110257e252bfSMichael Neumann 					/* update display watermarks based on new power state */
110357e252bfSMichael Neumann 					radeon_bandwidth_update(rdev);
110457e252bfSMichael Neumann 					/* update displays */
110557e252bfSMichael Neumann 					radeon_dpm_display_configuration_changed(rdev);
110657e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
110757e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
110857e252bfSMichael Neumann 					return;
110957e252bfSMichael Neumann 				}
111057e252bfSMichael Neumann 			}
111157e252bfSMichael Neumann 		}
111257e252bfSMichael Neumann 	}
111357e252bfSMichael Neumann 
1114c6f73aabSFrançois Tigeot force:
1115c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1) {
111657e252bfSMichael Neumann 		printk("switching from power state:\n");
111757e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
111857e252bfSMichael Neumann 		printk("switching to power state:\n");
111957e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
1120c6f73aabSFrançois Tigeot 	}
112157e252bfSMichael Neumann 
11221cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
11231cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
112457e252bfSMichael Neumann 
1125c6f73aabSFrançois Tigeot 	/* update whether vce is active */
1126c6f73aabSFrançois Tigeot 	ps->vce_active = rdev->pm.dpm.vce_active;
1127c6f73aabSFrançois Tigeot 
112857e252bfSMichael Neumann 	ret = radeon_dpm_pre_set_power_state(rdev);
112957e252bfSMichael Neumann 	if (ret)
113057e252bfSMichael Neumann 		goto done;
113157e252bfSMichael Neumann 
113257e252bfSMichael Neumann 	/* update display watermarks based on new power state */
113357e252bfSMichael Neumann 	radeon_bandwidth_update(rdev);
113457e252bfSMichael Neumann 	/* update displays */
113557e252bfSMichael Neumann 	radeon_dpm_display_configuration_changed(rdev);
113657e252bfSMichael Neumann 
113757e252bfSMichael Neumann 	/* wait for the rings to drain */
113857e252bfSMichael Neumann 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
113957e252bfSMichael Neumann 		struct radeon_ring *ring = &rdev->ring[i];
114057e252bfSMichael Neumann 		if (ring->ready)
1141c6f73aabSFrançois Tigeot 			radeon_fence_wait_empty(rdev, i);
114257e252bfSMichael Neumann 	}
114357e252bfSMichael Neumann 
114457e252bfSMichael Neumann 	/* program the new power state */
114557e252bfSMichael Neumann 	radeon_dpm_set_power_state(rdev);
114657e252bfSMichael Neumann 
114757e252bfSMichael Neumann 	/* update current power state */
114857e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
114957e252bfSMichael Neumann 
115057e252bfSMichael Neumann 	radeon_dpm_post_set_power_state(rdev);
115157e252bfSMichael Neumann 
1152*c59a5c48SFrançois Tigeot 	rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
1153*c59a5c48SFrançois Tigeot 	rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
1154*c59a5c48SFrançois Tigeot 	rdev->pm.dpm.single_display = single_display;
1155*c59a5c48SFrançois Tigeot 
11564cd92098Szrj 	if (rdev->asic->dpm.force_performance_level) {
1157c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
1158c6f73aabSFrançois Tigeot 			enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
11594cd92098Szrj 			/* force low perf level for thermal */
11604cd92098Szrj 			radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW);
1161c6f73aabSFrançois Tigeot 			/* save the user's level */
1162c6f73aabSFrançois Tigeot 			rdev->pm.dpm.forced_level = level;
1163c6f73aabSFrançois Tigeot 		} else {
1164c6f73aabSFrançois Tigeot 			/* otherwise, user selected level */
1165c6f73aabSFrançois Tigeot 			radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level);
1166c6f73aabSFrançois Tigeot 		}
11674cd92098Szrj 	}
11684cd92098Szrj 
116957e252bfSMichael Neumann done:
11701cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
11711cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
117257e252bfSMichael Neumann }
117357e252bfSMichael Neumann 
11744cd92098Szrj void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
117557e252bfSMichael Neumann {
11764cd92098Szrj 	enum radeon_pm_state_type dpm_state;
117757e252bfSMichael Neumann 
11784cd92098Szrj 	if (rdev->asic->dpm.powergate_uvd) {
11794cd92098Szrj 		mutex_lock(&rdev->pm.mutex);
1180c6f73aabSFrançois Tigeot 		/* don't powergate anything if we
1181c6f73aabSFrançois Tigeot 		   have active but pause streams */
1182c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.sd > 0;
1183c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.hd > 0;
11844cd92098Szrj 		/* enable/disable UVD */
11854cd92098Szrj 		radeon_dpm_powergate_uvd(rdev, !enable);
11864cd92098Szrj 		mutex_unlock(&rdev->pm.mutex);
11874cd92098Szrj 	} else {
11884cd92098Szrj 		if (enable) {
11894cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
119057e252bfSMichael Neumann 			rdev->pm.dpm.uvd_active = true;
11914cd92098Szrj 			/* disable this for now */
11924cd92098Szrj #if 0
11934cd92098Szrj 			if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
11944cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
11954cd92098Szrj 			else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
11964cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
11974cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1))
11984cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
11994cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
12004cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
12014cd92098Szrj 			else
12024cd92098Szrj #endif
12034cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
120457e252bfSMichael Neumann 			rdev->pm.dpm.state = dpm_state;
12054cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
12064cd92098Szrj 		} else {
12074cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
12084cd92098Szrj 			rdev->pm.dpm.uvd_active = false;
12094cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
12104cd92098Szrj 		}
12114cd92098Szrj 
121257e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
121357e252bfSMichael Neumann 	}
12144cd92098Szrj }
121557e252bfSMichael Neumann 
1216c6f73aabSFrançois Tigeot void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable)
1217c6f73aabSFrançois Tigeot {
1218c6f73aabSFrançois Tigeot 	if (enable) {
1219c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1220c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = true;
1221c6f73aabSFrançois Tigeot 		/* XXX select vce level based on ring/task */
1222c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL;
1223c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1224c6f73aabSFrançois Tigeot 	} else {
1225c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1226c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = false;
1227c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1228c6f73aabSFrançois Tigeot 	}
1229c6f73aabSFrançois Tigeot 
1230c6f73aabSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1231c6f73aabSFrançois Tigeot }
1232c6f73aabSFrançois Tigeot 
123357e252bfSMichael Neumann static void radeon_pm_suspend_old(struct radeon_device *rdev)
1234926deccbSFrançois Tigeot {
12351cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1236926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1237926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE)
1238926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED;
1239926deccbSFrançois Tigeot 	}
12401cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1241926deccbSFrançois Tigeot 
1242ee479021SImre Vadász #ifdef DUMBBELL_WIP
1243926deccbSFrançois Tigeot 	cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1244ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1245926deccbSFrançois Tigeot }
1246926deccbSFrançois Tigeot 
124757e252bfSMichael Neumann static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
124857e252bfSMichael Neumann {
12491cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
125057e252bfSMichael Neumann 	/* disable dpm */
125157e252bfSMichael Neumann 	radeon_dpm_disable(rdev);
125257e252bfSMichael Neumann 	/* reset the power state */
125357e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
125457e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
12551cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
125657e252bfSMichael Neumann }
125757e252bfSMichael Neumann 
125857e252bfSMichael Neumann void radeon_pm_suspend(struct radeon_device *rdev)
125957e252bfSMichael Neumann {
126057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
126157e252bfSMichael Neumann 		radeon_pm_suspend_dpm(rdev);
126257e252bfSMichael Neumann 	else
126357e252bfSMichael Neumann 		radeon_pm_suspend_old(rdev);
126457e252bfSMichael Neumann }
126557e252bfSMichael Neumann 
126657e252bfSMichael Neumann static void radeon_pm_resume_old(struct radeon_device *rdev)
1267926deccbSFrançois Tigeot {
1268926deccbSFrançois Tigeot 	/* set up the default clocks if the MC ucode is loaded */
1269926deccbSFrançois Tigeot 	if ((rdev->family >= CHIP_BARTS) &&
12704cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
1271926deccbSFrançois Tigeot 	    rdev->mc_fw) {
1272926deccbSFrançois Tigeot 		if (rdev->pm.default_vddc)
1273926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1274926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDC);
1275926deccbSFrançois Tigeot 		if (rdev->pm.default_vddci)
1276926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1277926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
1278926deccbSFrançois Tigeot 		if (rdev->pm.default_sclk)
1279926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1280926deccbSFrançois Tigeot 		if (rdev->pm.default_mclk)
1281926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1282926deccbSFrançois Tigeot 	}
1283926deccbSFrançois Tigeot 	/* asic init will reset the default power state */
12841cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1285926deccbSFrançois Tigeot 	rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
1286926deccbSFrançois Tigeot 	rdev->pm.current_clock_mode_index = 0;
1287926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->pm.default_sclk;
1288926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->pm.default_mclk;
1289c6f73aabSFrançois Tigeot 	if (rdev->pm.power_state) {
1290926deccbSFrançois Tigeot 		rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
1291926deccbSFrançois Tigeot 		rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
1292c6f73aabSFrançois Tigeot 	}
1293926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM
1294926deccbSFrançois Tigeot 	    && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
1295926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1296ee479021SImre Vadász #ifdef DUMBBELL_WIP
1297926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1298926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1299ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1300926deccbSFrançois Tigeot 	}
13011cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1302926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1303926deccbSFrançois Tigeot }
1304926deccbSFrançois Tigeot 
130557e252bfSMichael Neumann static void radeon_pm_resume_dpm(struct radeon_device *rdev)
1306926deccbSFrançois Tigeot {
1307926deccbSFrançois Tigeot 	int ret;
1308926deccbSFrançois Tigeot 
130957e252bfSMichael Neumann 	/* asic init will reset to the boot state */
13101cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
131157e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
131257e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
131357e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
13141cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1315c6f73aabSFrançois Tigeot 	if (ret)
1316c6f73aabSFrançois Tigeot 		goto dpm_resume_fail;
1317c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1318c6f73aabSFrançois Tigeot 	return;
1319c6f73aabSFrançois Tigeot 
1320c6f73aabSFrançois Tigeot dpm_resume_fail:
132157e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm resume failed\n");
132257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
13234cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
132457e252bfSMichael Neumann 	    rdev->mc_fw) {
132557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
132657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
132757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
132857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
132957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
133057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
133157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
133257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
133357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
133457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
133557e252bfSMichael Neumann 	}
133657e252bfSMichael Neumann }
133757e252bfSMichael Neumann 
133857e252bfSMichael Neumann void radeon_pm_resume(struct radeon_device *rdev)
133957e252bfSMichael Neumann {
134057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
134157e252bfSMichael Neumann 		radeon_pm_resume_dpm(rdev);
134257e252bfSMichael Neumann 	else
134357e252bfSMichael Neumann 		radeon_pm_resume_old(rdev);
134457e252bfSMichael Neumann }
134557e252bfSMichael Neumann 
134657e252bfSMichael Neumann static int radeon_pm_init_old(struct radeon_device *rdev)
134757e252bfSMichael Neumann {
134857e252bfSMichael Neumann 	int ret;
134957e252bfSMichael Neumann 
1350926deccbSFrançois Tigeot 	rdev->pm.profile = PM_PROFILE_DEFAULT;
1351926deccbSFrançois Tigeot 	rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1352926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1353926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_upclock = true;
1354926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_downclock = true;
1355926deccbSFrançois Tigeot 	rdev->pm.default_sclk = rdev->clock.default_sclk;
1356926deccbSFrançois Tigeot 	rdev->pm.default_mclk = rdev->clock.default_mclk;
1357926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->clock.default_sclk;
1358926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->clock.default_mclk;
1359926deccbSFrançois Tigeot 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
1360926deccbSFrançois Tigeot 
1361926deccbSFrançois Tigeot 	if (rdev->bios) {
1362926deccbSFrançois Tigeot 		if (rdev->is_atom_bios)
1363926deccbSFrançois Tigeot 			radeon_atombios_get_power_modes(rdev);
1364926deccbSFrançois Tigeot 		else
1365926deccbSFrançois Tigeot 			radeon_combios_get_power_modes(rdev);
1366926deccbSFrançois Tigeot 		radeon_pm_print_states(rdev);
1367926deccbSFrançois Tigeot 		radeon_pm_init_profile(rdev);
1368926deccbSFrançois Tigeot 		/* set up the default clocks if the MC ucode is loaded */
1369926deccbSFrançois Tigeot 		if ((rdev->family >= CHIP_BARTS) &&
13704cd92098Szrj 		    (rdev->family <= CHIP_CAYMAN) &&
1371926deccbSFrançois Tigeot 		    rdev->mc_fw) {
1372926deccbSFrançois Tigeot 			if (rdev->pm.default_vddc)
1373926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1374926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDC);
1375926deccbSFrançois Tigeot 			if (rdev->pm.default_vddci)
1376926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1377926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDCI);
1378926deccbSFrançois Tigeot 			if (rdev->pm.default_sclk)
1379926deccbSFrançois Tigeot 				radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1380926deccbSFrançois Tigeot 			if (rdev->pm.default_mclk)
1381926deccbSFrançois Tigeot 				radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1382926deccbSFrançois Tigeot 		}
1383926deccbSFrançois Tigeot 	}
1384926deccbSFrançois Tigeot 
1385926deccbSFrançois Tigeot 	/* set up the internal thermal sensor if applicable */
1386926deccbSFrançois Tigeot 	ret = radeon_hwmon_init(rdev);
1387926deccbSFrançois Tigeot 	if (ret)
1388926deccbSFrançois Tigeot 		return ret;
1389926deccbSFrançois Tigeot 
1390926deccbSFrançois Tigeot 	INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler);
1391926deccbSFrançois Tigeot 
1392926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1393926deccbSFrançois Tigeot 		if (radeon_debugfs_pm_init(rdev)) {
1394926deccbSFrançois Tigeot 			DRM_ERROR("Failed to register debugfs file for PM!\n");
1395926deccbSFrançois Tigeot 		}
1396926deccbSFrançois Tigeot 
1397926deccbSFrançois Tigeot 		DRM_INFO("radeon: power management initialized\n");
1398926deccbSFrançois Tigeot 	}
1399926deccbSFrançois Tigeot 
1400926deccbSFrançois Tigeot 	return 0;
1401926deccbSFrançois Tigeot }
1402926deccbSFrançois Tigeot 
140357e252bfSMichael Neumann static void radeon_dpm_print_power_states(struct radeon_device *rdev)
140457e252bfSMichael Neumann {
140557e252bfSMichael Neumann 	int i;
140657e252bfSMichael Neumann 
140757e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
140857e252bfSMichael Neumann 		printk("== power state %d ==\n", i);
140957e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
141057e252bfSMichael Neumann 	}
141157e252bfSMichael Neumann }
141257e252bfSMichael Neumann 
141357e252bfSMichael Neumann static int radeon_pm_init_dpm(struct radeon_device *rdev)
141457e252bfSMichael Neumann {
141557e252bfSMichael Neumann 	int ret;
141657e252bfSMichael Neumann 
14174cd92098Szrj 	/* default to balanced state */
141857e252bfSMichael Neumann 	rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
141957e252bfSMichael Neumann 	rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
14204cd92098Szrj 	rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
142157e252bfSMichael Neumann 	rdev->pm.default_sclk = rdev->clock.default_sclk;
142257e252bfSMichael Neumann 	rdev->pm.default_mclk = rdev->clock.default_mclk;
142357e252bfSMichael Neumann 	rdev->pm.current_sclk = rdev->clock.default_sclk;
142457e252bfSMichael Neumann 	rdev->pm.current_mclk = rdev->clock.default_mclk;
142557e252bfSMichael Neumann 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
142657e252bfSMichael Neumann 
142757e252bfSMichael Neumann 	if (rdev->bios && rdev->is_atom_bios)
142857e252bfSMichael Neumann 		radeon_atombios_get_power_modes(rdev);
142957e252bfSMichael Neumann 	else
143057e252bfSMichael Neumann 		return -EINVAL;
143157e252bfSMichael Neumann 
143257e252bfSMichael Neumann 	/* set up the internal thermal sensor if applicable */
143357e252bfSMichael Neumann 	ret = radeon_hwmon_init(rdev);
143457e252bfSMichael Neumann 	if (ret)
143557e252bfSMichael Neumann 		return ret;
143657e252bfSMichael Neumann 
14372c5cc6b9SFrançois Tigeot 	INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
14382c5cc6b9SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
143957e252bfSMichael Neumann 	radeon_dpm_init(rdev);
144057e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
1441c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1)
144257e252bfSMichael Neumann 		radeon_dpm_print_power_states(rdev);
144357e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
144457e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
14452c5cc6b9SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1446c6f73aabSFrançois Tigeot 	if (ret)
1447c6f73aabSFrançois Tigeot 		goto dpm_failed;
1448c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1449c6f73aabSFrançois Tigeot 
1450c6f73aabSFrançois Tigeot #ifdef TODO_DEVICE_FILE
1451c6f73aabSFrançois Tigeot 	if (radeon_debugfs_pm_init(rdev)) {
1452c6f73aabSFrançois Tigeot 		DRM_ERROR("Failed to register debugfs file for dpm!\n");
1453c6f73aabSFrançois Tigeot 	}
1454c6f73aabSFrançois Tigeot #endif
1455c6f73aabSFrançois Tigeot 
1456c6f73aabSFrançois Tigeot 	DRM_INFO("radeon: dpm initialized\n");
1457c6f73aabSFrançois Tigeot 
1458c6f73aabSFrançois Tigeot 	return 0;
1459c6f73aabSFrançois Tigeot 
1460c6f73aabSFrançois Tigeot dpm_failed:
146157e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
146257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
14634cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
146457e252bfSMichael Neumann 	    rdev->mc_fw) {
146557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
146657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
146757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
146857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
146957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
147057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
147157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
147257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
147357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
147457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
147557e252bfSMichael Neumann 	}
147657e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm initialization failed\n");
147757e252bfSMichael Neumann 	return ret;
147857e252bfSMichael Neumann }
147957e252bfSMichael Neumann 
14807dcf36dcSFrançois Tigeot struct radeon_dpm_quirk {
14817dcf36dcSFrançois Tigeot 	u32 chip_vendor;
14827dcf36dcSFrançois Tigeot 	u32 chip_device;
14837dcf36dcSFrançois Tigeot 	u32 subsys_vendor;
14847dcf36dcSFrançois Tigeot 	u32 subsys_device;
14857dcf36dcSFrançois Tigeot };
14867dcf36dcSFrançois Tigeot 
14877dcf36dcSFrançois Tigeot /* cards with dpm stability problems */
14887dcf36dcSFrançois Tigeot static struct radeon_dpm_quirk radeon_dpm_quirk_list[] = {
14897dcf36dcSFrançois Tigeot 	/* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */
14907dcf36dcSFrançois Tigeot 	{ PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 },
14917dcf36dcSFrançois Tigeot 	/* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */
14927dcf36dcSFrançois Tigeot 	{ PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 },
14937dcf36dcSFrançois Tigeot 	{ 0, 0, 0, 0 },
14947dcf36dcSFrançois Tigeot };
14957dcf36dcSFrançois Tigeot 
149657e252bfSMichael Neumann int radeon_pm_init(struct radeon_device *rdev)
149757e252bfSMichael Neumann {
14987dcf36dcSFrançois Tigeot 	struct radeon_dpm_quirk *p = radeon_dpm_quirk_list;
14997dcf36dcSFrançois Tigeot 	bool disable_dpm = false;
15007dcf36dcSFrançois Tigeot 
15017dcf36dcSFrançois Tigeot 	/* Apply dpm quirks */
15027dcf36dcSFrançois Tigeot 	while (p && p->chip_device != 0) {
15037dcf36dcSFrançois Tigeot 		if (rdev->pdev->vendor == p->chip_vendor &&
15047dcf36dcSFrançois Tigeot 		    rdev->pdev->device == p->chip_device &&
15057dcf36dcSFrançois Tigeot 		    rdev->pdev->subsystem_vendor == p->subsys_vendor &&
15067dcf36dcSFrançois Tigeot 		    rdev->pdev->subsystem_device == p->subsys_device) {
15077dcf36dcSFrançois Tigeot 			disable_dpm = true;
15087dcf36dcSFrançois Tigeot 			break;
15097dcf36dcSFrançois Tigeot 		}
15107dcf36dcSFrançois Tigeot 		++p;
15117dcf36dcSFrançois Tigeot 	}
15127dcf36dcSFrançois Tigeot 
151357e252bfSMichael Neumann 	/* enable dpm on rv6xx+ */
151457e252bfSMichael Neumann 	switch (rdev->family) {
151557e252bfSMichael Neumann 	case CHIP_RV610:
151657e252bfSMichael Neumann 	case CHIP_RV630:
151757e252bfSMichael Neumann 	case CHIP_RV620:
151857e252bfSMichael Neumann 	case CHIP_RV635:
151957e252bfSMichael Neumann 	case CHIP_RV670:
152057e252bfSMichael Neumann 	case CHIP_RS780:
152157e252bfSMichael Neumann 	case CHIP_RS880:
152257e252bfSMichael Neumann 	case CHIP_RV770:
1523c6f73aabSFrançois Tigeot 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
1524c6f73aabSFrançois Tigeot 		if (!rdev->rlc_fw)
1525c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1526c6f73aabSFrançois Tigeot 		else if ((rdev->family >= CHIP_RV770) &&
1527c6f73aabSFrançois Tigeot 			 (!(rdev->flags & RADEON_IS_IGP)) &&
1528c6f73aabSFrançois Tigeot 			 (!rdev->smc_fw))
1529c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1530c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 1)
1531c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
1532c6f73aabSFrançois Tigeot 		else
1533c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1534c6f73aabSFrançois Tigeot 		break;
153557e252bfSMichael Neumann 	case CHIP_RV730:
153657e252bfSMichael Neumann 	case CHIP_RV710:
153757e252bfSMichael Neumann 	case CHIP_RV740:
153857e252bfSMichael Neumann 	case CHIP_CEDAR:
153957e252bfSMichael Neumann 	case CHIP_REDWOOD:
154057e252bfSMichael Neumann 	case CHIP_JUNIPER:
154157e252bfSMichael Neumann 	case CHIP_CYPRESS:
154257e252bfSMichael Neumann 	case CHIP_HEMLOCK:
154357e252bfSMichael Neumann 	case CHIP_PALM:
154457e252bfSMichael Neumann 	case CHIP_SUMO:
154557e252bfSMichael Neumann 	case CHIP_SUMO2:
154657e252bfSMichael Neumann 	case CHIP_BARTS:
154757e252bfSMichael Neumann 	case CHIP_TURKS:
154857e252bfSMichael Neumann 	case CHIP_CAICOS:
154957e252bfSMichael Neumann 	case CHIP_CAYMAN:
155057e252bfSMichael Neumann 	case CHIP_ARUBA:
155157e252bfSMichael Neumann 	case CHIP_TAHITI:
155257e252bfSMichael Neumann 	case CHIP_PITCAIRN:
155357e252bfSMichael Neumann 	case CHIP_VERDE:
155457e252bfSMichael Neumann 	case CHIP_OLAND:
155557e252bfSMichael Neumann 	case CHIP_HAINAN:
15564cd92098Szrj 	case CHIP_BONAIRE:
15574cd92098Szrj 	case CHIP_KABINI:
15584cd92098Szrj 	case CHIP_KAVERI:
1559c6f73aabSFrançois Tigeot 	case CHIP_HAWAII:
1560c6f73aabSFrançois Tigeot 	case CHIP_MULLINS:
156157e252bfSMichael Neumann 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
156257e252bfSMichael Neumann 		if (!rdev->rlc_fw)
156357e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
156457e252bfSMichael Neumann 		else if ((rdev->family >= CHIP_RV770) &&
156557e252bfSMichael Neumann 			 (!(rdev->flags & RADEON_IS_IGP)) &&
156657e252bfSMichael Neumann 			 (!rdev->smc_fw))
156757e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
15687dcf36dcSFrançois Tigeot 		else if (disable_dpm && (radeon_dpm == -1))
15697dcf36dcSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1570c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 0)
157157e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1572c6f73aabSFrançois Tigeot 		else
1573c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
157457e252bfSMichael Neumann 		break;
157557e252bfSMichael Neumann 	default:
157657e252bfSMichael Neumann 		/* default to profile method */
157757e252bfSMichael Neumann 		rdev->pm.pm_method = PM_METHOD_PROFILE;
157857e252bfSMichael Neumann 		break;
157957e252bfSMichael Neumann 	}
158057e252bfSMichael Neumann 
158157e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
158257e252bfSMichael Neumann 		return radeon_pm_init_dpm(rdev);
158357e252bfSMichael Neumann 	else
158457e252bfSMichael Neumann 		return radeon_pm_init_old(rdev);
158557e252bfSMichael Neumann }
158657e252bfSMichael Neumann 
1587c6f73aabSFrançois Tigeot int radeon_pm_late_init(struct radeon_device *rdev)
1588c6f73aabSFrançois Tigeot {
1589c6f73aabSFrançois Tigeot 	int ret = 0;
1590c6f73aabSFrançois Tigeot 
1591c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
1592*c59a5c48SFrançois Tigeot 		if (rdev->pm.dpm_enabled) {
1593*c59a5c48SFrançois Tigeot 			if (!rdev->pm.sysfs_initialized) {
1594*c59a5c48SFrançois Tigeot #if 0
1595*c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
1596*c59a5c48SFrançois Tigeot 				if (ret)
1597*c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for dpm state\n");
1598*c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
1599*c59a5c48SFrançois Tigeot 				if (ret)
1600*c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for dpm state\n");
1601*c59a5c48SFrançois Tigeot 				/* XXX: these are noops for dpm but are here for backwards compat */
1602*c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1603*c59a5c48SFrançois Tigeot 				if (ret)
1604*c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for power profile\n");
1605*c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_method);
1606*c59a5c48SFrançois Tigeot 				if (ret)
1607*c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for power method\n");
1608*c59a5c48SFrançois Tigeot #endif
1609*c59a5c48SFrançois Tigeot 				rdev->pm.sysfs_initialized = true;
1610*c59a5c48SFrançois Tigeot 			}
1611*c59a5c48SFrançois Tigeot 
16121cfef1a5SFrançois Tigeot 			mutex_lock(&rdev->pm.mutex);
1613c6f73aabSFrançois Tigeot 			ret = radeon_dpm_late_enable(rdev);
16141cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->pm.mutex);
1615*c59a5c48SFrançois Tigeot 			if (ret) {
1616*c59a5c48SFrançois Tigeot 				rdev->pm.dpm_enabled = false;
1617*c59a5c48SFrançois Tigeot 				DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n");
1618*c59a5c48SFrançois Tigeot 			} else {
1619*c59a5c48SFrançois Tigeot 				/* set the dpm state for PX since there won't be
1620*c59a5c48SFrançois Tigeot 				 * a modeset to call this.
1621*c59a5c48SFrançois Tigeot 				 */
1622*c59a5c48SFrançois Tigeot 				radeon_pm_compute_clocks(rdev);
1623*c59a5c48SFrançois Tigeot 			}
1624*c59a5c48SFrançois Tigeot 		}
1625*c59a5c48SFrançois Tigeot 	} else {
1626*c59a5c48SFrançois Tigeot 		if ((rdev->pm.num_power_states > 1) &&
1627*c59a5c48SFrançois Tigeot 		    (!rdev->pm.sysfs_initialized)) {
1628*c59a5c48SFrançois Tigeot 			/* where's the best place to put these? */
1629*c59a5c48SFrançois Tigeot #if 0
1630*c59a5c48SFrançois Tigeot 			ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1631*c59a5c48SFrançois Tigeot 			if (ret)
1632*c59a5c48SFrançois Tigeot 				DRM_ERROR("failed to create device file for power profile\n");
1633*c59a5c48SFrançois Tigeot 			ret = device_create_file(rdev->dev, &dev_attr_power_method);
1634*c59a5c48SFrançois Tigeot 			if (ret)
1635*c59a5c48SFrançois Tigeot 				DRM_ERROR("failed to create device file for power method\n");
1636*c59a5c48SFrançois Tigeot 			if (!ret)
1637*c59a5c48SFrançois Tigeot 				rdev->pm.sysfs_initialized = true;
1638*c59a5c48SFrançois Tigeot #endif
1639*c59a5c48SFrançois Tigeot 		}
1640c6f73aabSFrançois Tigeot 	}
1641c6f73aabSFrançois Tigeot 	return ret;
1642c6f73aabSFrançois Tigeot }
1643c6f73aabSFrançois Tigeot 
164457e252bfSMichael Neumann static void radeon_pm_fini_old(struct radeon_device *rdev)
1645926deccbSFrançois Tigeot {
1646926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
16471cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1648926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1649926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
1650926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
1651926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1652926deccbSFrançois Tigeot 		} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1653926deccbSFrançois Tigeot 			/* reset default clocks */
1654926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1655926deccbSFrançois Tigeot 			rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1656926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1657926deccbSFrançois Tigeot 		}
16581cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1659926deccbSFrançois Tigeot 
1660926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1661ee479021SImre Vadász 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1662ee479021SImre Vadász 
1663926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_profile);
1664926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_method);
1665926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1666926deccbSFrançois Tigeot 	}
1667926deccbSFrançois Tigeot 
1668926deccbSFrançois Tigeot 	if (rdev->pm.power_state) {
1669926deccbSFrançois Tigeot 		int i;
1670926deccbSFrançois Tigeot 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
1671c4ef309bSzrj 			kfree(rdev->pm.power_state[i].clock_info);
1672926deccbSFrançois Tigeot 		}
1673c4ef309bSzrj 		kfree(rdev->pm.power_state);
1674926deccbSFrançois Tigeot 		rdev->pm.power_state = NULL;
1675926deccbSFrançois Tigeot 		rdev->pm.num_power_states = 0;
1676926deccbSFrançois Tigeot 	}
1677926deccbSFrançois Tigeot 
1678926deccbSFrançois Tigeot 	radeon_hwmon_fini(rdev);
1679926deccbSFrançois Tigeot }
1680926deccbSFrançois Tigeot 
168157e252bfSMichael Neumann static void radeon_pm_fini_dpm(struct radeon_device *rdev)
168257e252bfSMichael Neumann {
168357e252bfSMichael Neumann 	if (rdev->pm.num_power_states > 1) {
16841cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
168557e252bfSMichael Neumann 		radeon_dpm_disable(rdev);
16861cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
168757e252bfSMichael Neumann 
168857e252bfSMichael Neumann #ifdef TODO_DEVICE_FILE
168957e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
169057e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
169157e252bfSMichael Neumann 		/* XXX backwards compat */
169257e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_profile);
169357e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_method);
169457e252bfSMichael Neumann #endif
169557e252bfSMichael Neumann 	}
169657e252bfSMichael Neumann 	radeon_dpm_fini(rdev);
169757e252bfSMichael Neumann 
16985d6a9071Szrj 	/* prevents leaking 440 bytes on OLAND */
16995d6a9071Szrj 	if (rdev->pm.power_state) {
17005d6a9071Szrj 		int i;
17015d6a9071Szrj 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
17025d6a9071Szrj 			kfree(rdev->pm.power_state[i].clock_info);
17035d6a9071Szrj 		}
170457e252bfSMichael Neumann 		kfree(rdev->pm.power_state);
17055d6a9071Szrj 		rdev->pm.power_state = NULL;
17065d6a9071Szrj 		rdev->pm.num_power_states = 0;
17075d6a9071Szrj 	}
170857e252bfSMichael Neumann 
170957e252bfSMichael Neumann 	radeon_hwmon_fini(rdev);
171057e252bfSMichael Neumann }
171157e252bfSMichael Neumann 
171257e252bfSMichael Neumann void radeon_pm_fini(struct radeon_device *rdev)
171357e252bfSMichael Neumann {
171457e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
171557e252bfSMichael Neumann 		radeon_pm_fini_dpm(rdev);
171657e252bfSMichael Neumann 	else
171757e252bfSMichael Neumann 		radeon_pm_fini_old(rdev);
171857e252bfSMichael Neumann }
171957e252bfSMichael Neumann 
172057e252bfSMichael Neumann static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
1721926deccbSFrançois Tigeot {
1722926deccbSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1723926deccbSFrançois Tigeot 	struct drm_crtc *crtc;
1724926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc;
1725926deccbSFrançois Tigeot 
1726926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states < 2)
1727926deccbSFrançois Tigeot 		return;
1728926deccbSFrançois Tigeot 
17291cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1730926deccbSFrançois Tigeot 
1731926deccbSFrançois Tigeot 	rdev->pm.active_crtcs = 0;
1732926deccbSFrançois Tigeot 	rdev->pm.active_crtc_count = 0;
1733c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
1734926deccbSFrançois Tigeot 		list_for_each_entry(crtc,
1735926deccbSFrançois Tigeot 				    &ddev->mode_config.crtc_list, head) {
1736926deccbSFrançois Tigeot 			radeon_crtc = to_radeon_crtc(crtc);
1737926deccbSFrançois Tigeot 			if (radeon_crtc->enabled) {
1738926deccbSFrançois Tigeot 				rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
1739926deccbSFrançois Tigeot 				rdev->pm.active_crtc_count++;
1740926deccbSFrançois Tigeot 			}
1741926deccbSFrançois Tigeot 		}
1742c6f73aabSFrançois Tigeot 	}
1743926deccbSFrançois Tigeot 
1744926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1745926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
1746926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
1747926deccbSFrançois Tigeot 	} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1748926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) {
1749926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1) {
1750926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1751ee479021SImre Vadász #ifdef DUMBBELL_WIP
1752926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1753ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1754926deccbSFrançois Tigeot 
1755926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
1756926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1757926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1758926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1759926deccbSFrançois Tigeot 
1760926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n");
1761926deccbSFrançois Tigeot 				}
1762926deccbSFrançois Tigeot 			} else if (rdev->pm.active_crtc_count == 1) {
1763926deccbSFrançois Tigeot 				/* TODO: Increase clocks if needed for current mode */
1764926deccbSFrançois Tigeot 
1765926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) {
1766926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1767926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK;
1768926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1769926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1770926deccbSFrançois Tigeot 
1771ee479021SImre Vadász #ifdef DUMBBELL_WIP
1772926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1773926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1774ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1775926deccbSFrançois Tigeot 				} else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) {
1776926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1777ee479021SImre Vadász #ifdef DUMBBELL_WIP
1778926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1779926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1780ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1781926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n");
1782926deccbSFrançois Tigeot 				}
1783926deccbSFrançois Tigeot 			} else { /* count == 0 */
1784926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) {
1785ee479021SImre Vadász #ifdef DUMBBELL_WIP
1786926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1787ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1788926deccbSFrançois Tigeot 
1789926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM;
1790926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM;
1791926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1792926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1793926deccbSFrançois Tigeot 				}
1794926deccbSFrançois Tigeot 			}
1795926deccbSFrançois Tigeot 		}
1796926deccbSFrançois Tigeot 	}
1797926deccbSFrançois Tigeot 
17981cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1799926deccbSFrançois Tigeot }
1800926deccbSFrançois Tigeot 
180157e252bfSMichael Neumann static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
180257e252bfSMichael Neumann {
180357e252bfSMichael Neumann 	struct drm_device *ddev = rdev->ddev;
180457e252bfSMichael Neumann 	struct drm_crtc *crtc;
180557e252bfSMichael Neumann 	struct radeon_crtc *radeon_crtc;
180657e252bfSMichael Neumann 
1807c6f73aabSFrançois Tigeot 	if (!rdev->pm.dpm_enabled)
1808c6f73aabSFrançois Tigeot 		return;
1809c6f73aabSFrançois Tigeot 
18101cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
181157e252bfSMichael Neumann 
181257e252bfSMichael Neumann 	/* update active crtc counts */
181357e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtcs = 0;
181457e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtc_count = 0;
1815c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
181657e252bfSMichael Neumann 		list_for_each_entry(crtc,
181757e252bfSMichael Neumann 				    &ddev->mode_config.crtc_list, head) {
181857e252bfSMichael Neumann 			radeon_crtc = to_radeon_crtc(crtc);
181957e252bfSMichael Neumann 			if (crtc->enabled) {
182057e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
182157e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtc_count++;
182257e252bfSMichael Neumann 			}
182357e252bfSMichael Neumann 		}
1824c6f73aabSFrançois Tigeot 	}
182557e252bfSMichael Neumann 
182657e252bfSMichael Neumann 	/* update battery/ac status */
182757e252bfSMichael Neumann 	if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
182857e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = true;
182957e252bfSMichael Neumann 	else
183057e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = false;
183157e252bfSMichael Neumann 
183257e252bfSMichael Neumann 	radeon_dpm_change_power_state_locked(rdev);
183357e252bfSMichael Neumann 
18341cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1835c6f73aabSFrançois Tigeot 
183657e252bfSMichael Neumann }
183757e252bfSMichael Neumann 
183857e252bfSMichael Neumann void radeon_pm_compute_clocks(struct radeon_device *rdev)
183957e252bfSMichael Neumann {
184057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
184157e252bfSMichael Neumann 		radeon_pm_compute_clocks_dpm(rdev);
184257e252bfSMichael Neumann 	else
184357e252bfSMichael Neumann 		radeon_pm_compute_clocks_old(rdev);
184457e252bfSMichael Neumann }
184557e252bfSMichael Neumann 
1846926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev)
1847926deccbSFrançois Tigeot {
1848926deccbSFrançois Tigeot 	int  crtc, vpos, hpos, vbl_status;
1849926deccbSFrançois Tigeot 	bool in_vbl = true;
1850926deccbSFrançois Tigeot 
1851926deccbSFrançois Tigeot 	/* Iterate over all active crtc's. All crtc's must be in vblank,
1852926deccbSFrançois Tigeot 	 * otherwise return in_vbl == false.
1853926deccbSFrançois Tigeot 	 */
1854926deccbSFrançois Tigeot 	for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
1855926deccbSFrançois Tigeot 		if (rdev->pm.active_crtcs & (1 << crtc)) {
1856*c59a5c48SFrançois Tigeot 			vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev,
1857*c59a5c48SFrançois Tigeot 								crtc,
1858*c59a5c48SFrançois Tigeot 								USE_REAL_VBLANKSTART,
1859352ff8bdSFrançois Tigeot 								&vpos, &hpos, NULL, NULL,
1860352ff8bdSFrançois Tigeot 								&rdev->mode_info.crtcs[crtc]->base.hwmode);
1861926deccbSFrançois Tigeot 			if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
18621b13d190SFrançois Tigeot 			    !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK))
1863926deccbSFrançois Tigeot 				in_vbl = false;
1864926deccbSFrançois Tigeot 		}
1865926deccbSFrançois Tigeot 	}
1866926deccbSFrançois Tigeot 
1867926deccbSFrançois Tigeot 	return in_vbl;
1868926deccbSFrançois Tigeot }
1869926deccbSFrançois Tigeot 
1870926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
1871926deccbSFrançois Tigeot {
1872926deccbSFrançois Tigeot 	u32 stat_crtc = 0;
1873926deccbSFrançois Tigeot 	bool in_vbl = radeon_pm_in_vbl(rdev);
1874926deccbSFrançois Tigeot 
1875926deccbSFrançois Tigeot 	if (in_vbl == false)
1876926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc,
1877926deccbSFrançois Tigeot 			 finish ? "exit" : "entry");
1878926deccbSFrançois Tigeot 	return in_vbl;
1879926deccbSFrançois Tigeot }
1880926deccbSFrançois Tigeot 
1881926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work)
1882926deccbSFrançois Tigeot {
1883926deccbSFrançois Tigeot 	struct radeon_device *rdev;
1884926deccbSFrançois Tigeot 	int resched;
1885926deccbSFrançois Tigeot 	rdev = container_of(work, struct radeon_device,
1886926deccbSFrançois Tigeot 				pm.dynpm_idle_work.work);
1887926deccbSFrançois Tigeot 
1888926deccbSFrançois Tigeot 	resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
18891cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1890926deccbSFrançois Tigeot 	if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1891926deccbSFrançois Tigeot 		int not_processed = 0;
1892926deccbSFrançois Tigeot 		int i;
1893926deccbSFrançois Tigeot 
1894926deccbSFrançois Tigeot 		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
1895926deccbSFrançois Tigeot 			struct radeon_ring *ring = &rdev->ring[i];
1896926deccbSFrançois Tigeot 
1897926deccbSFrançois Tigeot 			if (ring->ready) {
1898926deccbSFrançois Tigeot 				not_processed += radeon_fence_count_emitted(rdev, i);
1899926deccbSFrançois Tigeot 				if (not_processed >= 3)
1900926deccbSFrançois Tigeot 					break;
1901926deccbSFrançois Tigeot 			}
1902926deccbSFrançois Tigeot 		}
1903926deccbSFrançois Tigeot 
1904926deccbSFrançois Tigeot 		if (not_processed >= 3) { /* should upclock */
1905926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) {
1906926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1907926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1908926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_upclock) {
1909926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1910926deccbSFrançois Tigeot 					DYNPM_ACTION_UPCLOCK;
1911926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1912926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1913926deccbSFrançois Tigeot 			}
1914926deccbSFrançois Tigeot 		} else if (not_processed == 0) { /* should downclock */
1915926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) {
1916926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1917926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1918926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_downclock) {
1919926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1920926deccbSFrançois Tigeot 					DYNPM_ACTION_DOWNCLOCK;
1921926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1922926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1923926deccbSFrançois Tigeot 			}
1924926deccbSFrançois Tigeot 		}
1925926deccbSFrançois Tigeot 
1926926deccbSFrançois Tigeot 		/* Note, radeon_pm_set_clocks is called with static_switch set
1927926deccbSFrançois Tigeot 		 * to false since we want to wait for vbl to avoid flicker.
1928926deccbSFrançois Tigeot 		 */
1929926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE &&
1930926deccbSFrançois Tigeot 		    jiffies > rdev->pm.dynpm_action_timeout) {
1931926deccbSFrançois Tigeot 			radeon_pm_get_dynpm_state(rdev);
1932926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1933926deccbSFrançois Tigeot 		}
1934926deccbSFrançois Tigeot 
1935926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1936926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1937926deccbSFrançois Tigeot 	}
19381cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1939926deccbSFrançois Tigeot 	ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
1940926deccbSFrançois Tigeot }
1941926deccbSFrançois Tigeot 
1942926deccbSFrançois Tigeot /*
1943926deccbSFrançois Tigeot  * Debugfs info
1944926deccbSFrançois Tigeot  */
1945926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1946926deccbSFrançois Tigeot 
1947926deccbSFrançois Tigeot static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
1948926deccbSFrançois Tigeot {
1949926deccbSFrançois Tigeot 	struct drm_info_node *node = (struct drm_info_node *) m->private;
1950926deccbSFrançois Tigeot 	struct drm_device *dev = node->minor->dev;
1951926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
1952c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1953926deccbSFrançois Tigeot 
1954c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
1955c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
1956c6f73aabSFrançois Tigeot 		seq_printf(m, "PX asic powered off\n");
1957c6f73aabSFrançois Tigeot 	} else if (rdev->pm.dpm_enabled) {
19581cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
195957e252bfSMichael Neumann 		if (rdev->asic->dpm.debugfs_print_current_performance_level)
196057e252bfSMichael Neumann 			radeon_dpm_debugfs_print_current_performance_level(rdev, m);
196157e252bfSMichael Neumann 		else
196257e252bfSMichael Neumann 			seq_printf(m, "Debugfs support not implemented for this asic\n");
19631cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
196457e252bfSMichael Neumann 	} else {
1965926deccbSFrançois Tigeot 		seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
1966f43cf1b1SMichael Neumann 		/* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
1967f43cf1b1SMichael Neumann 		if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
1968f43cf1b1SMichael Neumann 			seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
1969f43cf1b1SMichael Neumann 		else
1970926deccbSFrançois Tigeot 			seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
1971926deccbSFrançois Tigeot 		seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
1972926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_memory_clock)
1973926deccbSFrançois Tigeot 			seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
1974926deccbSFrançois Tigeot 		if (rdev->pm.current_vddc)
1975926deccbSFrançois Tigeot 			seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
1976926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_pcie_lanes)
1977926deccbSFrançois Tigeot 			seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
197857e252bfSMichael Neumann 	}
1979926deccbSFrançois Tigeot 
1980926deccbSFrançois Tigeot 	return 0;
1981926deccbSFrançois Tigeot }
1982926deccbSFrançois Tigeot 
1983926deccbSFrançois Tigeot static struct drm_info_list radeon_pm_info_list[] = {
1984926deccbSFrançois Tigeot 	{"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL},
1985926deccbSFrançois Tigeot };
1986926deccbSFrançois Tigeot #endif
1987926deccbSFrançois Tigeot 
1988926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev)
1989926deccbSFrançois Tigeot {
1990926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1991926deccbSFrançois Tigeot 	return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
1992926deccbSFrançois Tigeot #else
1993926deccbSFrançois Tigeot 	return 0;
1994926deccbSFrançois Tigeot #endif
1995926deccbSFrançois Tigeot }
1996