xref: /dflybsd-src/sys/dev/drm/radeon/radeon_pm.c (revision 26b5dbf28e0ee0c010f49f665fb8c6705da38a21)
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  * $FreeBSD: head/sys/dev/drm2/radeon/radeon_pm.c 254885 2013-08-25 19:37:15Z dumbbell $
24926deccbSFrançois Tigeot  */
25926deccbSFrançois Tigeot 
2657e252bfSMichael Neumann #include <sys/power.h>
27926deccbSFrançois Tigeot #include <drm/drmP.h>
282c86cb5bSImre Vadász #include <sys/sensors.h>
29926deccbSFrançois Tigeot #include "radeon.h"
30926deccbSFrançois Tigeot #include "avivod.h"
31926deccbSFrançois Tigeot #include "atom.h"
32926deccbSFrançois Tigeot 
33926deccbSFrançois Tigeot #define RADEON_IDLE_LOOP_MS 100
34926deccbSFrançois Tigeot #define RADEON_RECLOCK_DELAY_MS 200
35926deccbSFrançois Tigeot #define RADEON_WAIT_VBLANK_TIMEOUT 200
36926deccbSFrançois Tigeot 
37926deccbSFrançois Tigeot static const char *radeon_pm_state_type_name[5] = {
38926deccbSFrançois Tigeot 	"",
39926deccbSFrançois Tigeot 	"Powersave",
40926deccbSFrançois Tigeot 	"Battery",
41926deccbSFrançois Tigeot 	"Balanced",
42926deccbSFrançois Tigeot 	"Performance",
43926deccbSFrançois Tigeot };
44926deccbSFrançois Tigeot 
45926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
46926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work);
47926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
48926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev);
49926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev);
50926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
51926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev);
52926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev);
53926deccbSFrançois Tigeot 
54926deccbSFrançois Tigeot int radeon_pm_get_type_index(struct radeon_device *rdev,
55926deccbSFrançois Tigeot 			     enum radeon_pm_state_type ps_type,
56926deccbSFrançois Tigeot 			     int instance)
57926deccbSFrançois Tigeot {
58926deccbSFrançois Tigeot 	int i;
59926deccbSFrançois Tigeot 	int found_instance = -1;
60926deccbSFrançois Tigeot 
61926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
62926deccbSFrançois Tigeot 		if (rdev->pm.power_state[i].type == ps_type) {
63926deccbSFrançois Tigeot 			found_instance++;
64926deccbSFrançois Tigeot 			if (found_instance == instance)
65926deccbSFrançois Tigeot 				return i;
66926deccbSFrançois Tigeot 		}
67926deccbSFrançois Tigeot 	}
68926deccbSFrançois Tigeot 	/* return default if no match */
69926deccbSFrançois Tigeot 	return rdev->pm.default_power_state_index;
70926deccbSFrançois Tigeot }
71926deccbSFrançois Tigeot 
72926deccbSFrançois Tigeot void radeon_pm_acpi_event_handler(struct radeon_device *rdev)
73926deccbSFrançois Tigeot {
744cd92098Szrj 	if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
754cd92098Szrj 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
76c6f73aabSFrançois Tigeot 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
774cd92098Szrj 			rdev->pm.dpm.ac_power = true;
784cd92098Szrj 		else
794cd92098Szrj 			rdev->pm.dpm.ac_power = false;
80c6f73aabSFrançois Tigeot 		if (rdev->family == CHIP_ARUBA) {
814cd92098Szrj 			if (rdev->asic->dpm.enable_bapm)
824cd92098Szrj 				radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power);
83c6f73aabSFrançois Tigeot 		}
844cd92098Szrj 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
854cd92098Szrj         } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
86926deccbSFrançois Tigeot 		if (rdev->pm.profile == PM_PROFILE_AUTO) {
87926deccbSFrançois Tigeot 			lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
88926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
89926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
90926deccbSFrançois Tigeot 			lockmgr(&rdev->pm.mutex, LK_RELEASE);
91926deccbSFrançois Tigeot 		}
92926deccbSFrançois Tigeot 	}
93926deccbSFrançois Tigeot }
94926deccbSFrançois Tigeot 
95926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev)
96926deccbSFrançois Tigeot {
97926deccbSFrançois Tigeot 	switch (rdev->pm.profile) {
98926deccbSFrançois Tigeot 	case PM_PROFILE_DEFAULT:
99926deccbSFrançois Tigeot 		rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX;
100926deccbSFrançois Tigeot 		break;
101926deccbSFrançois Tigeot 	case PM_PROFILE_AUTO:
10257e252bfSMichael Neumann 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) {
103926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
104926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
105926deccbSFrançois Tigeot 			else
106926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
107926deccbSFrançois Tigeot 		} else {
108926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
109926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
110926deccbSFrançois Tigeot 			else
111926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
112926deccbSFrançois Tigeot 		}
113926deccbSFrançois Tigeot 		break;
114926deccbSFrançois Tigeot 	case PM_PROFILE_LOW:
115926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
116926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX;
117926deccbSFrançois Tigeot 		else
118926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX;
119926deccbSFrançois Tigeot 		break;
120926deccbSFrançois Tigeot 	case PM_PROFILE_MID:
121926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
122926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
123926deccbSFrançois Tigeot 		else
124926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
125926deccbSFrançois Tigeot 		break;
126926deccbSFrançois Tigeot 	case PM_PROFILE_HIGH:
127926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
128926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
129926deccbSFrançois Tigeot 		else
130926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
131926deccbSFrançois Tigeot 		break;
132926deccbSFrançois Tigeot 	}
133926deccbSFrançois Tigeot 
134926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count == 0) {
135926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
136926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx;
137926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
138926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx;
139926deccbSFrançois Tigeot 	} else {
140926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
141926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx;
142926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
143926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx;
144926deccbSFrançois Tigeot 	}
145926deccbSFrançois Tigeot }
146926deccbSFrançois Tigeot 
147926deccbSFrançois Tigeot static void radeon_unmap_vram_bos(struct radeon_device *rdev)
148926deccbSFrançois Tigeot {
149926deccbSFrançois Tigeot 	struct radeon_bo *bo, *n;
150926deccbSFrançois Tigeot 
151926deccbSFrançois Tigeot 	if (list_empty(&rdev->gem.objects))
152926deccbSFrançois Tigeot 		return;
153926deccbSFrançois Tigeot 
154926deccbSFrançois Tigeot 	list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
155926deccbSFrançois Tigeot 		if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
156926deccbSFrançois Tigeot 			ttm_bo_unmap_virtual(&bo->tbo);
157926deccbSFrançois Tigeot 	}
158926deccbSFrançois Tigeot }
159926deccbSFrançois Tigeot 
160926deccbSFrançois Tigeot static void radeon_sync_with_vblank(struct radeon_device *rdev)
161926deccbSFrançois Tigeot {
162926deccbSFrançois Tigeot 	if (rdev->pm.active_crtcs) {
163926deccbSFrançois Tigeot 		rdev->pm.vblank_sync = false;
164926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
165926deccbSFrançois Tigeot 		wait_event_timeout(
166926deccbSFrançois Tigeot 			rdev->irq.vblank_queue, rdev->pm.vblank_sync,
167926deccbSFrançois Tigeot 			msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
168926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
169926deccbSFrançois Tigeot 	}
170926deccbSFrançois Tigeot }
171926deccbSFrançois Tigeot 
172926deccbSFrançois Tigeot static void radeon_set_power_state(struct radeon_device *rdev)
173926deccbSFrançois Tigeot {
174926deccbSFrançois Tigeot 	u32 sclk, mclk;
175926deccbSFrançois Tigeot 	bool misc_after = false;
176926deccbSFrançois Tigeot 
177926deccbSFrançois Tigeot 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
178926deccbSFrançois Tigeot 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
179926deccbSFrançois Tigeot 		return;
180926deccbSFrançois Tigeot 
181926deccbSFrançois Tigeot 	if (radeon_gui_idle(rdev)) {
182926deccbSFrançois Tigeot 		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
183926deccbSFrançois Tigeot 			clock_info[rdev->pm.requested_clock_mode_index].sclk;
184926deccbSFrançois Tigeot 		if (sclk > rdev->pm.default_sclk)
185926deccbSFrançois Tigeot 			sclk = rdev->pm.default_sclk;
186926deccbSFrançois Tigeot 
187926deccbSFrançois Tigeot 		/* starting with BTC, there is one state that is used for both
188926deccbSFrançois Tigeot 		 * MH and SH.  Difference is that we always use the high clock index for
189b403bed8SMichael Neumann 		 * mclk and vddci.
190926deccbSFrançois Tigeot 		 */
191926deccbSFrançois Tigeot 		if ((rdev->pm.pm_method == PM_METHOD_PROFILE) &&
192926deccbSFrançois Tigeot 		    (rdev->family >= CHIP_BARTS) &&
193926deccbSFrançois Tigeot 		    rdev->pm.active_crtc_count &&
194926deccbSFrançois Tigeot 		    ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) ||
195926deccbSFrançois Tigeot 		     (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX)))
196926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
197926deccbSFrançois Tigeot 				clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk;
198926deccbSFrançois Tigeot 		else
199926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
200926deccbSFrançois Tigeot 				clock_info[rdev->pm.requested_clock_mode_index].mclk;
201926deccbSFrançois Tigeot 
202926deccbSFrançois Tigeot 		if (mclk > rdev->pm.default_mclk)
203926deccbSFrançois Tigeot 			mclk = rdev->pm.default_mclk;
204926deccbSFrançois Tigeot 
205926deccbSFrançois Tigeot 		/* upvolt before raising clocks, downvolt after lowering clocks */
206926deccbSFrançois Tigeot 		if (sclk < rdev->pm.current_sclk)
207926deccbSFrançois Tigeot 			misc_after = true;
208926deccbSFrançois Tigeot 
209926deccbSFrançois Tigeot 		radeon_sync_with_vblank(rdev);
210926deccbSFrançois Tigeot 
211926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
212926deccbSFrançois Tigeot 			if (!radeon_pm_in_vbl(rdev))
213926deccbSFrançois Tigeot 				return;
214926deccbSFrançois Tigeot 		}
215926deccbSFrançois Tigeot 
216926deccbSFrançois Tigeot 		radeon_pm_prepare(rdev);
217926deccbSFrançois Tigeot 
218926deccbSFrançois Tigeot 		if (!misc_after)
219926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
220926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
221926deccbSFrançois Tigeot 
222926deccbSFrançois Tigeot 		/* set engine clock */
223926deccbSFrançois Tigeot 		if (sclk != rdev->pm.current_sclk) {
224926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
225926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, sclk);
226926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
227926deccbSFrançois Tigeot 			rdev->pm.current_sclk = sclk;
228926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk);
229926deccbSFrançois Tigeot 		}
230926deccbSFrançois Tigeot 
231926deccbSFrançois Tigeot 		/* set memory clock */
232926deccbSFrançois Tigeot 		if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) {
233926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
234926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, mclk);
235926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
236926deccbSFrançois Tigeot 			rdev->pm.current_mclk = mclk;
237926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk);
238926deccbSFrançois Tigeot 		}
239926deccbSFrançois Tigeot 
240926deccbSFrançois Tigeot 		if (misc_after)
241926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
242926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
243926deccbSFrançois Tigeot 
244926deccbSFrançois Tigeot 		radeon_pm_finish(rdev);
245926deccbSFrançois Tigeot 
246926deccbSFrançois Tigeot 		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
247926deccbSFrançois Tigeot 		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
248926deccbSFrançois Tigeot 	} else
249926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n");
250926deccbSFrançois Tigeot }
251926deccbSFrançois Tigeot 
252926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev)
253926deccbSFrançois Tigeot {
254926deccbSFrançois Tigeot 	int i, r;
255926deccbSFrançois Tigeot 
256926deccbSFrançois Tigeot 	/* no need to take locks, etc. if nothing's going to change */
257926deccbSFrançois Tigeot 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
258926deccbSFrançois Tigeot 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
259926deccbSFrançois Tigeot 		return;
260926deccbSFrançois Tigeot 
261926deccbSFrançois Tigeot 	DRM_LOCK(rdev->ddev);
26257e252bfSMichael Neumann 	lockmgr(&rdev->pm.mclk_lock, LK_EXCLUSIVE); // down_write
263926deccbSFrançois Tigeot 	lockmgr(&rdev->ring_lock, LK_EXCLUSIVE);
264926deccbSFrançois Tigeot 
265926deccbSFrançois Tigeot 	/* wait for the rings to drain */
266926deccbSFrançois Tigeot 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
267926deccbSFrançois Tigeot 		struct radeon_ring *ring = &rdev->ring[i];
268926deccbSFrançois Tigeot 		if (!ring->ready) {
269926deccbSFrançois Tigeot 			continue;
270926deccbSFrançois Tigeot 		}
271c6f73aabSFrançois Tigeot 		r = radeon_fence_wait_empty(rdev, i);
272926deccbSFrançois Tigeot 		if (r) {
273926deccbSFrançois Tigeot 			/* needs a GPU reset dont reset here */
274926deccbSFrançois Tigeot 			lockmgr(&rdev->ring_lock, LK_RELEASE);
27557e252bfSMichael Neumann 			lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write
276926deccbSFrançois Tigeot 			DRM_UNLOCK(rdev->ddev);
277926deccbSFrançois Tigeot 			return;
278926deccbSFrançois Tigeot 		}
279926deccbSFrançois Tigeot 	}
280926deccbSFrançois Tigeot 
281926deccbSFrançois Tigeot 	radeon_unmap_vram_bos(rdev);
282926deccbSFrançois Tigeot 
283926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
284926deccbSFrançois Tigeot 		for (i = 0; i < rdev->num_crtc; i++) {
285926deccbSFrançois Tigeot 			if (rdev->pm.active_crtcs & (1 << i)) {
286926deccbSFrançois Tigeot 				rdev->pm.req_vblank |= (1 << i);
287926deccbSFrançois Tigeot 				drm_vblank_get(rdev->ddev, i);
288926deccbSFrançois Tigeot 			}
289926deccbSFrançois Tigeot 		}
290926deccbSFrançois Tigeot 	}
291926deccbSFrançois Tigeot 
292926deccbSFrançois Tigeot 	radeon_set_power_state(rdev);
293926deccbSFrançois Tigeot 
294926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
295926deccbSFrançois Tigeot 		for (i = 0; i < rdev->num_crtc; i++) {
296926deccbSFrançois Tigeot 			if (rdev->pm.req_vblank & (1 << i)) {
297926deccbSFrançois Tigeot 				rdev->pm.req_vblank &= ~(1 << i);
298926deccbSFrançois Tigeot 				drm_vblank_put(rdev->ddev, i);
299926deccbSFrançois Tigeot 			}
300926deccbSFrançois Tigeot 		}
301926deccbSFrançois Tigeot 	}
302926deccbSFrançois Tigeot 
303926deccbSFrançois Tigeot 	/* update display watermarks based on new power state */
304926deccbSFrançois Tigeot 	radeon_update_bandwidth_info(rdev);
305926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count)
306926deccbSFrançois Tigeot 		radeon_bandwidth_update(rdev);
307926deccbSFrançois Tigeot 
308926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
309926deccbSFrançois Tigeot 
310926deccbSFrançois Tigeot 	lockmgr(&rdev->ring_lock, LK_RELEASE);
31157e252bfSMichael Neumann 	lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write
312926deccbSFrançois Tigeot 	DRM_UNLOCK(rdev->ddev);
313926deccbSFrançois Tigeot }
314926deccbSFrançois Tigeot 
315926deccbSFrançois Tigeot static void radeon_pm_print_states(struct radeon_device *rdev)
316926deccbSFrançois Tigeot {
317926deccbSFrançois Tigeot 	int i, j;
318926deccbSFrançois Tigeot 	struct radeon_power_state *power_state;
319926deccbSFrançois Tigeot 	struct radeon_pm_clock_info *clock_info;
320926deccbSFrançois Tigeot 
321926deccbSFrançois Tigeot 	DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states);
322926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
323926deccbSFrançois Tigeot 		power_state = &rdev->pm.power_state[i];
324926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("State %d: %s\n", i,
325926deccbSFrançois Tigeot 			radeon_pm_state_type_name[power_state->type]);
326926deccbSFrançois Tigeot 		if (i == rdev->pm.default_power_state_index)
327926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tDefault");
328926deccbSFrançois Tigeot 		if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
329926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes);
330926deccbSFrançois Tigeot 		if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY)
331926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tSingle display only\n");
332926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes);
333926deccbSFrançois Tigeot 		for (j = 0; j < power_state->num_clock_modes; j++) {
334926deccbSFrançois Tigeot 			clock_info = &(power_state->clock_info[j]);
335926deccbSFrançois Tigeot 			if (rdev->flags & RADEON_IS_IGP)
336926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\n",
337926deccbSFrançois Tigeot 						 j,
338926deccbSFrançois Tigeot 						 clock_info->sclk * 10);
339926deccbSFrançois Tigeot 			else
340926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n",
341926deccbSFrançois Tigeot 						 j,
342926deccbSFrançois Tigeot 						 clock_info->sclk * 10,
343926deccbSFrançois Tigeot 						 clock_info->mclk * 10,
344926deccbSFrançois Tigeot 						 clock_info->voltage.voltage);
345926deccbSFrançois Tigeot 		}
346926deccbSFrançois Tigeot 	}
347926deccbSFrançois Tigeot }
348926deccbSFrançois Tigeot 
349926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
350926deccbSFrançois Tigeot static ssize_t radeon_get_pm_profile(struct device *dev,
351926deccbSFrançois Tigeot 				     struct device_attribute *attr,
352926deccbSFrançois Tigeot 				     char *buf)
353926deccbSFrançois Tigeot {
354c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
355926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
356926deccbSFrançois Tigeot 	int cp = rdev->pm.profile;
357926deccbSFrançois Tigeot 
358926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
359926deccbSFrançois Tigeot 			(cp == PM_PROFILE_AUTO) ? "auto" :
360926deccbSFrançois Tigeot 			(cp == PM_PROFILE_LOW) ? "low" :
361926deccbSFrançois Tigeot 			(cp == PM_PROFILE_MID) ? "mid" :
362926deccbSFrançois Tigeot 			(cp == PM_PROFILE_HIGH) ? "high" : "default");
363926deccbSFrançois Tigeot }
364926deccbSFrançois Tigeot 
365926deccbSFrançois Tigeot static ssize_t radeon_set_pm_profile(struct device *dev,
366926deccbSFrançois Tigeot 				     struct device_attribute *attr,
367926deccbSFrançois Tigeot 				     const char *buf,
368926deccbSFrançois Tigeot 				     size_t count)
369926deccbSFrançois Tigeot {
370c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
371926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
372926deccbSFrançois Tigeot 
373c6f73aabSFrançois Tigeot 	/* Can't set profile when the card is off */
374c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
375c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
376c6f73aabSFrançois Tigeot 		return -EINVAL;
377c6f73aabSFrançois Tigeot 
378926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
379926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
380926deccbSFrançois Tigeot 		if (strncmp("default", buf, strlen("default")) == 0)
381926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
382926deccbSFrançois Tigeot 		else if (strncmp("auto", buf, strlen("auto")) == 0)
383926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_AUTO;
384926deccbSFrançois Tigeot 		else if (strncmp("low", buf, strlen("low")) == 0)
385926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_LOW;
386926deccbSFrançois Tigeot 		else if (strncmp("mid", buf, strlen("mid")) == 0)
387926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_MID;
388926deccbSFrançois Tigeot 		else if (strncmp("high", buf, strlen("high")) == 0)
389926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_HIGH;
390926deccbSFrançois Tigeot 		else {
391926deccbSFrançois Tigeot 			count = -EINVAL;
392926deccbSFrançois Tigeot 			goto fail;
393926deccbSFrançois Tigeot 		}
394926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
395926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
396926deccbSFrançois Tigeot 	} else
397926deccbSFrançois Tigeot 		count = -EINVAL;
398926deccbSFrançois Tigeot 
399926deccbSFrançois Tigeot fail:
400926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
401926deccbSFrançois Tigeot 
402926deccbSFrançois Tigeot 	return count;
403926deccbSFrançois Tigeot }
404926deccbSFrançois Tigeot 
405926deccbSFrançois Tigeot static ssize_t radeon_get_pm_method(struct device *dev,
406926deccbSFrançois Tigeot 				    struct device_attribute *attr,
407926deccbSFrançois Tigeot 				    char *buf)
408926deccbSFrançois Tigeot {
409c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
410926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
411926deccbSFrançois Tigeot 	int pm = rdev->pm.pm_method;
412926deccbSFrançois Tigeot 
413926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
41457e252bfSMichael Neumann 			(pm == PM_METHOD_DYNPM) ? "dynpm" :
41557e252bfSMichael Neumann 			(pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
416926deccbSFrançois Tigeot }
417926deccbSFrançois Tigeot 
418926deccbSFrançois Tigeot static ssize_t radeon_set_pm_method(struct device *dev,
419926deccbSFrançois Tigeot 				    struct device_attribute *attr,
420926deccbSFrançois Tigeot 				    const char *buf,
421926deccbSFrançois Tigeot 				    size_t count)
422926deccbSFrançois Tigeot {
423c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
424926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
425926deccbSFrançois Tigeot 
426c6f73aabSFrançois Tigeot 	/* Can't set method when the card is off */
427c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
428c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
429c6f73aabSFrançois Tigeot 		count = -EINVAL;
430c6f73aabSFrançois Tigeot 		goto fail;
431c6f73aabSFrançois Tigeot 	}
432c6f73aabSFrançois Tigeot 
43357e252bfSMichael Neumann 	/* we don't support the legacy modes with dpm */
43457e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
43557e252bfSMichael Neumann 		count = -EINVAL;
43657e252bfSMichael Neumann 		goto fail;
43757e252bfSMichael Neumann 	}
438926deccbSFrançois Tigeot 
439926deccbSFrançois Tigeot 	if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
440926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
441926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_DYNPM;
442926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
443926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
444926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
445926deccbSFrançois Tigeot 	} else if (strncmp("profile", buf, strlen("profile")) == 0) {
446926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
447926deccbSFrançois Tigeot 		/* disable dynpm */
448926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
449926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
450926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_PROFILE;
451926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
452926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
453926deccbSFrançois Tigeot 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
454926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
455926deccbSFrançois Tigeot 	} else {
456926deccbSFrançois Tigeot 		count = -EINVAL;
457926deccbSFrançois Tigeot 		goto fail;
458926deccbSFrançois Tigeot 	}
459926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
460926deccbSFrançois Tigeot fail:
461926deccbSFrançois Tigeot 	return count;
462926deccbSFrançois Tigeot }
463926deccbSFrançois Tigeot 
46457e252bfSMichael Neumann static ssize_t radeon_get_dpm_state(struct device *dev,
46557e252bfSMichael Neumann 				    struct device_attribute *attr,
46657e252bfSMichael Neumann 				    char *buf)
46757e252bfSMichael Neumann {
468c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
46957e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
47057e252bfSMichael Neumann 	enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
47157e252bfSMichael Neumann 
47257e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
47357e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
47457e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
47557e252bfSMichael Neumann }
47657e252bfSMichael Neumann 
47757e252bfSMichael Neumann static ssize_t radeon_set_dpm_state(struct device *dev,
47857e252bfSMichael Neumann 				    struct device_attribute *attr,
47957e252bfSMichael Neumann 				    const char *buf,
48057e252bfSMichael Neumann 				    size_t count)
48157e252bfSMichael Neumann {
482c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
48357e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
48457e252bfSMichael Neumann 
48557e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
48657e252bfSMichael Neumann 	if (strncmp("battery", buf, strlen("battery")) == 0)
48757e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
48857e252bfSMichael Neumann 	else if (strncmp("balanced", buf, strlen("balanced")) == 0)
48957e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
49057e252bfSMichael Neumann 	else if (strncmp("performance", buf, strlen("performance")) == 0)
49157e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
49257e252bfSMichael Neumann 	else {
49357e252bfSMichael Neumann 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
49457e252bfSMichael Neumann 		count = -EINVAL;
49557e252bfSMichael Neumann 		goto fail;
49657e252bfSMichael Neumann 	}
49757e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
498c6f73aabSFrançois Tigeot 
499c6f73aabSFrançois Tigeot 	/* Can't set dpm state when the card is off */
500c6f73aabSFrançois Tigeot 	if (!(rdev->flags & RADEON_IS_PX) ||
501c6f73aabSFrançois Tigeot 	    (ddev->switch_power_state == DRM_SWITCH_POWER_ON))
50257e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
503c6f73aabSFrançois Tigeot 
50457e252bfSMichael Neumann fail:
50557e252bfSMichael Neumann 	return count;
50657e252bfSMichael Neumann }
50757e252bfSMichael Neumann 
50857e252bfSMichael Neumann static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
50957e252bfSMichael Neumann 						       struct device_attribute *attr,
51057e252bfSMichael Neumann 						       char *buf)
51157e252bfSMichael Neumann {
512c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
51357e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
51457e252bfSMichael Neumann 	enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
51557e252bfSMichael Neumann 
516c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
517c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
518c6f73aabSFrançois Tigeot 		return snprintf(buf, PAGE_SIZE, "off\n");
519c6f73aabSFrançois Tigeot 
52057e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
52157e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
52257e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
52357e252bfSMichael Neumann }
52457e252bfSMichael Neumann 
52557e252bfSMichael Neumann static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
52657e252bfSMichael Neumann 						       struct device_attribute *attr,
52757e252bfSMichael Neumann 						       const char *buf,
52857e252bfSMichael Neumann 						       size_t count)
52957e252bfSMichael Neumann {
530c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
53157e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
53257e252bfSMichael Neumann 	enum radeon_dpm_forced_level level;
53357e252bfSMichael Neumann 	int ret = 0;
53457e252bfSMichael Neumann 
535c6f73aabSFrançois Tigeot 	/* Can't force performance level when the card is off */
536c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
537c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
538c6f73aabSFrançois Tigeot 		return -EINVAL;
539c6f73aabSFrançois Tigeot 
54057e252bfSMichael Neumann 	spin_lock(&rdev->pm.mutex);
54157e252bfSMichael Neumann 	if (strncmp("low", buf, strlen("low")) == 0) {
54257e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_LOW;
54357e252bfSMichael Neumann 	} else if (strncmp("high", buf, strlen("high")) == 0) {
54457e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_HIGH;
54557e252bfSMichael Neumann 	} else if (strncmp("auto", buf, strlen("auto")) == 0) {
54657e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_AUTO;
54757e252bfSMichael Neumann 	} else {
54857e252bfSMichael Neumann 		count = -EINVAL;
54957e252bfSMichael Neumann 		goto fail;
55057e252bfSMichael Neumann 	}
55157e252bfSMichael Neumann 	if (rdev->asic->dpm.force_performance_level) {
552c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
553c6f73aabSFrançois Tigeot 			count = -EINVAL;
554c6f73aabSFrançois Tigeot 			goto fail;
555c6f73aabSFrançois Tigeot 		}
55657e252bfSMichael Neumann 		ret = radeon_dpm_force_performance_level(rdev, level);
55757e252bfSMichael Neumann 		if (ret)
55857e252bfSMichael Neumann 			count = -EINVAL;
55957e252bfSMichael Neumann 	}
56057e252bfSMichael Neumann fail:
561c6f73aabSFrançois Tigeot 	spin_unlock(&rdev->pm.mutex);
562c6f73aabSFrançois Tigeot 
56357e252bfSMichael Neumann 	return count;
56457e252bfSMichael Neumann }
56557e252bfSMichael Neumann 
566926deccbSFrançois Tigeot static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
567926deccbSFrançois Tigeot static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
56857e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
56957e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
57057e252bfSMichael Neumann 		   radeon_get_dpm_forced_performance_level,
57157e252bfSMichael Neumann 		   radeon_set_dpm_forced_performance_level);
572926deccbSFrançois Tigeot 
573926deccbSFrançois Tigeot static ssize_t radeon_hwmon_show_temp(struct device *dev,
574926deccbSFrançois Tigeot 				      struct device_attribute *attr,
575926deccbSFrançois Tigeot 				      char *buf)
576926deccbSFrançois Tigeot {
577c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
578c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
579926deccbSFrançois Tigeot 	int temp;
580926deccbSFrançois Tigeot 
581c6f73aabSFrançois Tigeot 	/* Can't get temperature when the card is off */
582c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
583c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
584c6f73aabSFrançois Tigeot 		return -EINVAL;
585c6f73aabSFrançois Tigeot 
58657e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature)
58757e252bfSMichael Neumann 		temp = radeon_get_temperature(rdev);
58857e252bfSMichael Neumann 	else
589926deccbSFrançois Tigeot 		temp = 0;
590926deccbSFrançois Tigeot 
591926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
592926deccbSFrançois Tigeot }
593926deccbSFrançois Tigeot 
594c6f73aabSFrançois Tigeot static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev,
595926deccbSFrançois Tigeot 					     struct device_attribute *attr,
596926deccbSFrançois Tigeot 					     char *buf)
597926deccbSFrançois Tigeot {
598c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
599c6f73aabSFrançois Tigeot 	int hyst = to_sensor_dev_attr(attr)->index;
600c6f73aabSFrançois Tigeot 	int temp;
601c6f73aabSFrançois Tigeot 
602c6f73aabSFrançois Tigeot 	if (hyst)
603c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.min_temp;
604c6f73aabSFrançois Tigeot 	else
605c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.max_temp;
606c6f73aabSFrançois Tigeot 
607c6f73aabSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
608926deccbSFrançois Tigeot }
609926deccbSFrançois Tigeot 
610926deccbSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0);
611c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0);
612c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1);
613926deccbSFrançois Tigeot 
614926deccbSFrançois Tigeot static struct attribute *hwmon_attributes[] = {
615926deccbSFrançois Tigeot 	&sensor_dev_attr_temp1_input.dev_attr.attr,
616c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
617c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
618926deccbSFrançois Tigeot 	NULL
619926deccbSFrançois Tigeot };
620926deccbSFrançois Tigeot 
621c6f73aabSFrançois Tigeot static umode_t hwmon_attributes_visible(struct kobject *kobj,
622c6f73aabSFrançois Tigeot 					struct attribute *attr, int index)
623c6f73aabSFrançois Tigeot {
624c6f73aabSFrançois Tigeot 	struct device *dev = container_of(kobj, struct device, kobj);
625c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
626c6f73aabSFrançois Tigeot 
627c6f73aabSFrançois Tigeot 	/* Skip limit attributes if DPM is not enabled */
628c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method != PM_METHOD_DPM &&
629c6f73aabSFrançois Tigeot 	    (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr ||
630c6f73aabSFrançois Tigeot 	     attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr))
631c6f73aabSFrançois Tigeot 		return 0;
632c6f73aabSFrançois Tigeot 
633c6f73aabSFrançois Tigeot 	return attr->mode;
634c6f73aabSFrançois Tigeot }
635c6f73aabSFrançois Tigeot 
636926deccbSFrançois Tigeot static const struct attribute_group hwmon_attrgroup = {
637926deccbSFrançois Tigeot 	.attrs = hwmon_attributes,
638c6f73aabSFrançois Tigeot 	.is_visible = hwmon_attributes_visible,
639c6f73aabSFrançois Tigeot };
640c6f73aabSFrançois Tigeot 
641c6f73aabSFrançois Tigeot static const struct attribute_group *hwmon_groups[] = {
642c6f73aabSFrançois Tigeot 	&hwmon_attrgroup,
643c6f73aabSFrançois Tigeot 	NULL
644926deccbSFrançois Tigeot };
645926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
646926deccbSFrançois Tigeot 
6472c86cb5bSImre Vadász static void
6482c86cb5bSImre Vadász radeon_hwmon_refresh(void *arg)
6492c86cb5bSImre Vadász {
6502c86cb5bSImre Vadász 	struct radeon_device *rdev = (struct radeon_device *)arg;
651*26b5dbf2SImre Vadász 	struct drm_device *ddev = rdev->ddev;
6522c86cb5bSImre Vadász 	struct ksensor *s = rdev->pm.int_sensor;
6532c86cb5bSImre Vadász 	int temp;
654*26b5dbf2SImre Vadász 	enum sensor_status stat;
6552c86cb5bSImre Vadász 
656*26b5dbf2SImre Vadász 	/* Can't get temperature when the card is off */
657*26b5dbf2SImre Vadász 	if  ((rdev->flags & RADEON_IS_PX) &&
658*26b5dbf2SImre Vadász 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
659*26b5dbf2SImre Vadász 		sensor_set_unknown(s);
660*26b5dbf2SImre Vadász 		s->status = SENSOR_S_OK;
661*26b5dbf2SImre Vadász 		return;
662*26b5dbf2SImre Vadász 	}
663*26b5dbf2SImre Vadász 
664*26b5dbf2SImre Vadász 	if (rdev->asic->pm.get_temperature == NULL) {
665*26b5dbf2SImre Vadász 		sensor_set_invalid(s);
666*26b5dbf2SImre Vadász 		return;
667*26b5dbf2SImre Vadász 	}
668*26b5dbf2SImre Vadász 
6692c86cb5bSImre Vadász 	temp = radeon_get_temperature(rdev);
670*26b5dbf2SImre Vadász 	if (temp >= rdev->pm.dpm.thermal.max_temp)
671*26b5dbf2SImre Vadász 		stat = SENSOR_S_CRIT;
672*26b5dbf2SImre Vadász 	else if (temp >= rdev->pm.dpm.thermal.min_temp)
673*26b5dbf2SImre Vadász 		stat = SENSOR_S_WARN;
6742c86cb5bSImre Vadász 	else
675*26b5dbf2SImre Vadász 		stat = SENSOR_S_OK;
6762c86cb5bSImre Vadász 
677*26b5dbf2SImre Vadász 	sensor_set(s, temp * 1000 + 273150000, stat);
6782c86cb5bSImre Vadász }
6792c86cb5bSImre Vadász 
680926deccbSFrançois Tigeot static int radeon_hwmon_init(struct radeon_device *rdev)
681926deccbSFrançois Tigeot {
682926deccbSFrançois Tigeot 	int err = 0;
683926deccbSFrançois Tigeot 
6842c86cb5bSImre Vadász 	rdev->pm.int_sensor = NULL;
6852c86cb5bSImre Vadász 	rdev->pm.int_sensordev = NULL;
686926deccbSFrançois Tigeot 
687926deccbSFrançois Tigeot 	switch (rdev->pm.int_thermal_type) {
688926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV6XX:
689926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV770:
690926deccbSFrançois Tigeot 	case THERMAL_TYPE_EVERGREEN:
691926deccbSFrançois Tigeot 	case THERMAL_TYPE_NI:
692926deccbSFrançois Tigeot 	case THERMAL_TYPE_SUMO:
693926deccbSFrançois Tigeot 	case THERMAL_TYPE_SI:
6944cd92098Szrj 	case THERMAL_TYPE_CI:
6954cd92098Szrj 	case THERMAL_TYPE_KV:
69657e252bfSMichael Neumann 		if (rdev->asic->pm.get_temperature == NULL)
697926deccbSFrançois Tigeot 			return err;
6982c86cb5bSImre Vadász 
6992c86cb5bSImre Vadász 		rdev->pm.int_sensor = kmalloc(sizeof(*rdev->pm.int_sensor),
7002c86cb5bSImre Vadász 		    M_DRM, M_ZERO | M_WAITOK);
7012c86cb5bSImre Vadász 		rdev->pm.int_sensordev = kmalloc(
7022c86cb5bSImre Vadász 		    sizeof(*rdev->pm.int_sensordev), M_DRM,
7032c86cb5bSImre Vadász 		    M_ZERO | M_WAITOK);
7042c86cb5bSImre Vadász 		strlcpy(rdev->pm.int_sensordev->xname,
7052c86cb5bSImre Vadász 		    device_get_nameunit(rdev->dev),
7062c86cb5bSImre Vadász 		    sizeof(rdev->pm.int_sensordev->xname));
7072c86cb5bSImre Vadász 		rdev->pm.int_sensor->type = SENSOR_TEMP;
708*26b5dbf2SImre Vadász 		rdev->pm.int_sensor->flags |= SENSOR_FINVALID;
7092c86cb5bSImre Vadász 		sensor_attach(rdev->pm.int_sensordev, rdev->pm.int_sensor);
7102c86cb5bSImre Vadász 		sensor_task_register(rdev, radeon_hwmon_refresh, 5);
7112c86cb5bSImre Vadász 		sensordev_install(rdev->pm.int_sensordev);
712926deccbSFrançois Tigeot 		break;
713926deccbSFrançois Tigeot 	default:
714926deccbSFrançois Tigeot 		break;
715926deccbSFrançois Tigeot 	}
716926deccbSFrançois Tigeot 
717926deccbSFrançois Tigeot 	return err;
718926deccbSFrançois Tigeot }
719926deccbSFrançois Tigeot 
720926deccbSFrançois Tigeot static void radeon_hwmon_fini(struct radeon_device *rdev)
721926deccbSFrançois Tigeot {
7222c86cb5bSImre Vadász 	if (rdev->pm.int_sensor != NULL && rdev->pm.int_sensordev != NULL) {
7232c86cb5bSImre Vadász 		sensordev_deinstall(rdev->pm.int_sensordev);
7242c86cb5bSImre Vadász 		sensor_task_unregister(rdev);
7252c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensor);
7262c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensordev);
7272c86cb5bSImre Vadász 		rdev->pm.int_sensor = NULL;
7282c86cb5bSImre Vadász 		rdev->pm.int_sensordev = NULL;
729926deccbSFrançois Tigeot 	}
730926deccbSFrançois Tigeot }
731926deccbSFrançois Tigeot 
73257e252bfSMichael Neumann static void radeon_dpm_thermal_work_handler(void *arg, int pending)
73357e252bfSMichael Neumann {
73457e252bfSMichael Neumann 	struct radeon_device *rdev = arg;
73557e252bfSMichael Neumann 	/* switch to the thermal state */
73657e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
73757e252bfSMichael Neumann 
73857e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
73957e252bfSMichael Neumann 		return;
74057e252bfSMichael Neumann 
74157e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature) {
74257e252bfSMichael Neumann 		int temp = radeon_get_temperature(rdev);
74357e252bfSMichael Neumann 
74457e252bfSMichael Neumann 		if (temp < rdev->pm.dpm.thermal.min_temp)
74557e252bfSMichael Neumann 			/* switch back the user state */
74657e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
74757e252bfSMichael Neumann 	} else {
74857e252bfSMichael Neumann 		if (rdev->pm.dpm.thermal.high_to_low)
74957e252bfSMichael Neumann 			/* switch back the user state */
75057e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
75157e252bfSMichael Neumann 	}
7524cd92098Szrj 	mutex_lock(&rdev->pm.mutex);
7534cd92098Szrj 	if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL)
7544cd92098Szrj 		rdev->pm.dpm.thermal_active = true;
7554cd92098Szrj 	else
7564cd92098Szrj 		rdev->pm.dpm.thermal_active = false;
7574cd92098Szrj 	rdev->pm.dpm.state = dpm_state;
7584cd92098Szrj 	mutex_unlock(&rdev->pm.mutex);
7594cd92098Szrj 
7604cd92098Szrj 	radeon_pm_compute_clocks(rdev);
76157e252bfSMichael Neumann }
76257e252bfSMichael Neumann 
76357e252bfSMichael Neumann static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
76457e252bfSMichael Neumann 						     enum radeon_pm_state_type dpm_state)
76557e252bfSMichael Neumann {
76657e252bfSMichael Neumann 	int i;
76757e252bfSMichael Neumann 	struct radeon_ps *ps;
76857e252bfSMichael Neumann 	u32 ui_class;
76957e252bfSMichael Neumann 	bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
77057e252bfSMichael Neumann 		true : false;
77157e252bfSMichael Neumann 
77257e252bfSMichael Neumann 	/* check if the vblank period is too short to adjust the mclk */
77357e252bfSMichael Neumann 	if (single_display && rdev->asic->dpm.vblank_too_short) {
77457e252bfSMichael Neumann 		if (radeon_dpm_vblank_too_short(rdev))
77557e252bfSMichael Neumann 			single_display = false;
77657e252bfSMichael Neumann 	}
77757e252bfSMichael Neumann 
77857e252bfSMichael Neumann 	/* certain older asics have a separare 3D performance state,
77957e252bfSMichael Neumann 	 * so try that first if the user selected performance
78057e252bfSMichael Neumann 	 */
78157e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
78257e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
78357e252bfSMichael Neumann 	/* balanced states don't exist at the moment */
78457e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_BALANCED)
78557e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
78657e252bfSMichael Neumann 
78757e252bfSMichael Neumann restart_search:
78857e252bfSMichael Neumann 	/* Pick the best power state based on current conditions */
78957e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
79057e252bfSMichael Neumann 		ps = &rdev->pm.dpm.ps[i];
79157e252bfSMichael Neumann 		ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
79257e252bfSMichael Neumann 		switch (dpm_state) {
79357e252bfSMichael Neumann 		/* user states */
79457e252bfSMichael Neumann 		case POWER_STATE_TYPE_BATTERY:
79557e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
79657e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
79757e252bfSMichael Neumann 					if (single_display)
79857e252bfSMichael Neumann 						return ps;
79957e252bfSMichael Neumann 				} else
80057e252bfSMichael Neumann 					return ps;
80157e252bfSMichael Neumann 			}
80257e252bfSMichael Neumann 			break;
80357e252bfSMichael Neumann 		case POWER_STATE_TYPE_BALANCED:
80457e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
80557e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
80657e252bfSMichael Neumann 					if (single_display)
80757e252bfSMichael Neumann 						return ps;
80857e252bfSMichael Neumann 				} else
80957e252bfSMichael Neumann 					return ps;
81057e252bfSMichael Neumann 			}
81157e252bfSMichael Neumann 			break;
81257e252bfSMichael Neumann 		case POWER_STATE_TYPE_PERFORMANCE:
81357e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
81457e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
81557e252bfSMichael Neumann 					if (single_display)
81657e252bfSMichael Neumann 						return ps;
81757e252bfSMichael Neumann 				} else
81857e252bfSMichael Neumann 					return ps;
81957e252bfSMichael Neumann 			}
82057e252bfSMichael Neumann 			break;
82157e252bfSMichael Neumann 		/* internal states */
82257e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD:
8234cd92098Szrj 			if (rdev->pm.dpm.uvd_ps)
82457e252bfSMichael Neumann 				return rdev->pm.dpm.uvd_ps;
8254cd92098Szrj 			else
8264cd92098Szrj 				break;
82757e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_SD:
82857e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
82957e252bfSMichael Neumann 				return ps;
83057e252bfSMichael Neumann 			break;
83157e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD:
83257e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
83357e252bfSMichael Neumann 				return ps;
83457e252bfSMichael Neumann 			break;
83557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
83657e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
83757e252bfSMichael Neumann 				return ps;
83857e252bfSMichael Neumann 			break;
83957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
84057e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
84157e252bfSMichael Neumann 				return ps;
84257e252bfSMichael Neumann 			break;
84357e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_BOOT:
84457e252bfSMichael Neumann 			return rdev->pm.dpm.boot_ps;
84557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_THERMAL:
84657e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
84757e252bfSMichael Neumann 				return ps;
84857e252bfSMichael Neumann 			break;
84957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ACPI:
85057e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
85157e252bfSMichael Neumann 				return ps;
85257e252bfSMichael Neumann 			break;
85357e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ULV:
85457e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
85557e252bfSMichael Neumann 				return ps;
85657e252bfSMichael Neumann 			break;
85757e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_3DPERF:
85857e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
85957e252bfSMichael Neumann 				return ps;
86057e252bfSMichael Neumann 			break;
86157e252bfSMichael Neumann 		default:
86257e252bfSMichael Neumann 			break;
86357e252bfSMichael Neumann 		}
86457e252bfSMichael Neumann 	}
86557e252bfSMichael Neumann 	/* use a fallback state if we didn't match */
86657e252bfSMichael Neumann 	switch (dpm_state) {
86757e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_SD:
8684cd92098Szrj 		dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
8694cd92098Szrj 		goto restart_search;
87057e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD:
87157e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
87257e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
8734cd92098Szrj 		if (rdev->pm.dpm.uvd_ps) {
87457e252bfSMichael Neumann 			return rdev->pm.dpm.uvd_ps;
8754cd92098Szrj 		} else {
8764cd92098Szrj 			dpm_state = POWER_STATE_TYPE_PERFORMANCE;
8774cd92098Szrj 			goto restart_search;
8784cd92098Szrj 		}
87957e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_THERMAL:
88057e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
88157e252bfSMichael Neumann 		goto restart_search;
88257e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_ACPI:
88357e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_BATTERY;
88457e252bfSMichael Neumann 		goto restart_search;
88557e252bfSMichael Neumann 	case POWER_STATE_TYPE_BATTERY:
88657e252bfSMichael Neumann 	case POWER_STATE_TYPE_BALANCED:
88757e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_3DPERF:
88857e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
88957e252bfSMichael Neumann 		goto restart_search;
89057e252bfSMichael Neumann 	default:
89157e252bfSMichael Neumann 		break;
89257e252bfSMichael Neumann 	}
89357e252bfSMichael Neumann 
89457e252bfSMichael Neumann 	return NULL;
89557e252bfSMichael Neumann }
89657e252bfSMichael Neumann 
89757e252bfSMichael Neumann static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
89857e252bfSMichael Neumann {
89957e252bfSMichael Neumann 	int i;
90057e252bfSMichael Neumann 	struct radeon_ps *ps;
90157e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state;
90257e252bfSMichael Neumann 	int ret;
90357e252bfSMichael Neumann 
90457e252bfSMichael Neumann 	/* if dpm init failed */
90557e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
90657e252bfSMichael Neumann 		return;
90757e252bfSMichael Neumann 
90857e252bfSMichael Neumann 	if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
90957e252bfSMichael Neumann 		/* add other state override checks here */
91057e252bfSMichael Neumann 		if ((!rdev->pm.dpm.thermal_active) &&
91157e252bfSMichael Neumann 		    (!rdev->pm.dpm.uvd_active))
91257e252bfSMichael Neumann 			rdev->pm.dpm.state = rdev->pm.dpm.user_state;
91357e252bfSMichael Neumann 	}
91457e252bfSMichael Neumann 	dpm_state = rdev->pm.dpm.state;
91557e252bfSMichael Neumann 
91657e252bfSMichael Neumann 	ps = radeon_dpm_pick_power_state(rdev, dpm_state);
91757e252bfSMichael Neumann 	if (ps)
91857e252bfSMichael Neumann 		rdev->pm.dpm.requested_ps = ps;
91957e252bfSMichael Neumann 	else
92057e252bfSMichael Neumann 		return;
92157e252bfSMichael Neumann 
92257e252bfSMichael Neumann 	/* no need to reprogram if nothing changed unless we are on BTC+ */
92357e252bfSMichael Neumann 	if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
924c6f73aabSFrançois Tigeot 		/* vce just modifies an existing state so force a change */
925c6f73aabSFrançois Tigeot 		if (ps->vce_active != rdev->pm.dpm.vce_active)
926c6f73aabSFrançois Tigeot 			goto force;
92757e252bfSMichael Neumann 		if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
92857e252bfSMichael Neumann 			/* for pre-BTC and APUs if the num crtcs changed but state is the same,
92957e252bfSMichael Neumann 			 * all we need to do is update the display configuration.
93057e252bfSMichael Neumann 			 */
93157e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
93257e252bfSMichael Neumann 				/* update display watermarks based on new power state */
93357e252bfSMichael Neumann 				radeon_bandwidth_update(rdev);
93457e252bfSMichael Neumann 				/* update displays */
93557e252bfSMichael Neumann 				radeon_dpm_display_configuration_changed(rdev);
93657e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
93757e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
93857e252bfSMichael Neumann 			}
93957e252bfSMichael Neumann 			return;
94057e252bfSMichael Neumann 		} else {
94157e252bfSMichael Neumann 			/* for BTC+ if the num crtcs hasn't changed and state is the same,
94257e252bfSMichael Neumann 			 * nothing to do, if the num crtcs is > 1 and state is the same,
94357e252bfSMichael Neumann 			 * update display configuration.
94457e252bfSMichael Neumann 			 */
94557e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs ==
94657e252bfSMichael Neumann 			    rdev->pm.dpm.current_active_crtcs) {
94757e252bfSMichael Neumann 				return;
94857e252bfSMichael Neumann 			} else {
94957e252bfSMichael Neumann 				if ((rdev->pm.dpm.current_active_crtc_count > 1) &&
95057e252bfSMichael Neumann 				    (rdev->pm.dpm.new_active_crtc_count > 1)) {
95157e252bfSMichael Neumann 					/* update display watermarks based on new power state */
95257e252bfSMichael Neumann 					radeon_bandwidth_update(rdev);
95357e252bfSMichael Neumann 					/* update displays */
95457e252bfSMichael Neumann 					radeon_dpm_display_configuration_changed(rdev);
95557e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
95657e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
95757e252bfSMichael Neumann 					return;
95857e252bfSMichael Neumann 				}
95957e252bfSMichael Neumann 			}
96057e252bfSMichael Neumann 		}
96157e252bfSMichael Neumann 	}
96257e252bfSMichael Neumann 
963c6f73aabSFrançois Tigeot force:
964c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1) {
96557e252bfSMichael Neumann 		printk("switching from power state:\n");
96657e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
96757e252bfSMichael Neumann 		printk("switching to power state:\n");
96857e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
969c6f73aabSFrançois Tigeot 	}
97057e252bfSMichael Neumann 
97157e252bfSMichael Neumann 	lockmgr(&rdev->ddev->struct_mutex, LK_EXCLUSIVE);
97257e252bfSMichael Neumann 	lockmgr(&rdev->pm.mclk_lock, LK_EXCLUSIVE); // down_write
97357e252bfSMichael Neumann 	lockmgr(&rdev->ring_lock, LK_EXCLUSIVE);
97457e252bfSMichael Neumann 
975c6f73aabSFrançois Tigeot 	/* update whether vce is active */
976c6f73aabSFrançois Tigeot 	ps->vce_active = rdev->pm.dpm.vce_active;
977c6f73aabSFrançois Tigeot 
97857e252bfSMichael Neumann 	ret = radeon_dpm_pre_set_power_state(rdev);
97957e252bfSMichael Neumann 	if (ret)
98057e252bfSMichael Neumann 		goto done;
98157e252bfSMichael Neumann 
98257e252bfSMichael Neumann 	/* update display watermarks based on new power state */
98357e252bfSMichael Neumann 	radeon_bandwidth_update(rdev);
98457e252bfSMichael Neumann 	/* update displays */
98557e252bfSMichael Neumann 	radeon_dpm_display_configuration_changed(rdev);
98657e252bfSMichael Neumann 
98757e252bfSMichael Neumann 	rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
98857e252bfSMichael Neumann 	rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
98957e252bfSMichael Neumann 
99057e252bfSMichael Neumann 	/* wait for the rings to drain */
99157e252bfSMichael Neumann 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
99257e252bfSMichael Neumann 		struct radeon_ring *ring = &rdev->ring[i];
99357e252bfSMichael Neumann 		if (ring->ready)
994c6f73aabSFrançois Tigeot 			radeon_fence_wait_empty(rdev, i);
99557e252bfSMichael Neumann 	}
99657e252bfSMichael Neumann 
99757e252bfSMichael Neumann 	/* program the new power state */
99857e252bfSMichael Neumann 	radeon_dpm_set_power_state(rdev);
99957e252bfSMichael Neumann 
100057e252bfSMichael Neumann 	/* update current power state */
100157e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
100257e252bfSMichael Neumann 
100357e252bfSMichael Neumann 	radeon_dpm_post_set_power_state(rdev);
100457e252bfSMichael Neumann 
10054cd92098Szrj 	if (rdev->asic->dpm.force_performance_level) {
1006c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
1007c6f73aabSFrançois Tigeot 			enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
10084cd92098Szrj 			/* force low perf level for thermal */
10094cd92098Szrj 			radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW);
1010c6f73aabSFrançois Tigeot 			/* save the user's level */
1011c6f73aabSFrançois Tigeot 			rdev->pm.dpm.forced_level = level;
1012c6f73aabSFrançois Tigeot 		} else {
1013c6f73aabSFrançois Tigeot 			/* otherwise, user selected level */
1014c6f73aabSFrançois Tigeot 			radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level);
1015c6f73aabSFrançois Tigeot 		}
10164cd92098Szrj 	}
10174cd92098Szrj 
101857e252bfSMichael Neumann done:
101957e252bfSMichael Neumann 	lockmgr(&rdev->ring_lock, LK_RELEASE);
102057e252bfSMichael Neumann 	lockmgr(&rdev->pm.mclk_lock, LK_RELEASE); // up_write
102157e252bfSMichael Neumann 	lockmgr(&rdev->ddev->struct_mutex, LK_RELEASE);
102257e252bfSMichael Neumann }
102357e252bfSMichael Neumann 
10244cd92098Szrj void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
102557e252bfSMichael Neumann {
10264cd92098Szrj 	enum radeon_pm_state_type dpm_state;
102757e252bfSMichael Neumann 
10284cd92098Szrj 	if (rdev->asic->dpm.powergate_uvd) {
10294cd92098Szrj 		mutex_lock(&rdev->pm.mutex);
1030c6f73aabSFrançois Tigeot 		/* don't powergate anything if we
1031c6f73aabSFrançois Tigeot 		   have active but pause streams */
1032c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.sd > 0;
1033c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.hd > 0;
10344cd92098Szrj 		/* enable/disable UVD */
10354cd92098Szrj 		radeon_dpm_powergate_uvd(rdev, !enable);
10364cd92098Szrj 		mutex_unlock(&rdev->pm.mutex);
10374cd92098Szrj 	} else {
10384cd92098Szrj 		if (enable) {
10394cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
104057e252bfSMichael Neumann 			rdev->pm.dpm.uvd_active = true;
10414cd92098Szrj 			/* disable this for now */
10424cd92098Szrj #if 0
10434cd92098Szrj 			if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
10444cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
10454cd92098Szrj 			else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
10464cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10474cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1))
10484cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10494cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
10504cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
10514cd92098Szrj 			else
10524cd92098Szrj #endif
10534cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
105457e252bfSMichael Neumann 			rdev->pm.dpm.state = dpm_state;
10554cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
10564cd92098Szrj 		} else {
10574cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
10584cd92098Szrj 			rdev->pm.dpm.uvd_active = false;
10594cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
10604cd92098Szrj 		}
10614cd92098Szrj 
106257e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
106357e252bfSMichael Neumann 	}
10644cd92098Szrj }
106557e252bfSMichael Neumann 
1066c6f73aabSFrançois Tigeot void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable)
1067c6f73aabSFrançois Tigeot {
1068c6f73aabSFrançois Tigeot 	if (enable) {
1069c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1070c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = true;
1071c6f73aabSFrançois Tigeot 		/* XXX select vce level based on ring/task */
1072c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL;
1073c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1074c6f73aabSFrançois Tigeot 	} else {
1075c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1076c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = false;
1077c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1078c6f73aabSFrançois Tigeot 	}
1079c6f73aabSFrançois Tigeot 
1080c6f73aabSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1081c6f73aabSFrançois Tigeot }
1082c6f73aabSFrançois Tigeot 
108357e252bfSMichael Neumann static void radeon_pm_suspend_old(struct radeon_device *rdev)
1084926deccbSFrançois Tigeot {
1085926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1086926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1087926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE)
1088926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED;
1089926deccbSFrançois Tigeot 	}
1090926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1091926deccbSFrançois Tigeot 
1092926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1093926deccbSFrançois Tigeot 	cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1094926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1095926deccbSFrançois Tigeot }
1096926deccbSFrançois Tigeot 
109757e252bfSMichael Neumann static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
109857e252bfSMichael Neumann {
109957e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
110057e252bfSMichael Neumann 	/* disable dpm */
110157e252bfSMichael Neumann 	radeon_dpm_disable(rdev);
110257e252bfSMichael Neumann 	/* reset the power state */
110357e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
110457e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
110557e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
110657e252bfSMichael Neumann }
110757e252bfSMichael Neumann 
110857e252bfSMichael Neumann void radeon_pm_suspend(struct radeon_device *rdev)
110957e252bfSMichael Neumann {
111057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
111157e252bfSMichael Neumann 		radeon_pm_suspend_dpm(rdev);
111257e252bfSMichael Neumann 	else
111357e252bfSMichael Neumann 		radeon_pm_suspend_old(rdev);
111457e252bfSMichael Neumann }
111557e252bfSMichael Neumann 
111657e252bfSMichael Neumann static void radeon_pm_resume_old(struct radeon_device *rdev)
1117926deccbSFrançois Tigeot {
1118926deccbSFrançois Tigeot 	/* set up the default clocks if the MC ucode is loaded */
1119926deccbSFrançois Tigeot 	if ((rdev->family >= CHIP_BARTS) &&
11204cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
1121926deccbSFrançois Tigeot 	    rdev->mc_fw) {
1122926deccbSFrançois Tigeot 		if (rdev->pm.default_vddc)
1123926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1124926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDC);
1125926deccbSFrançois Tigeot 		if (rdev->pm.default_vddci)
1126926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1127926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
1128926deccbSFrançois Tigeot 		if (rdev->pm.default_sclk)
1129926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1130926deccbSFrançois Tigeot 		if (rdev->pm.default_mclk)
1131926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1132926deccbSFrançois Tigeot 	}
1133926deccbSFrançois Tigeot 	/* asic init will reset the default power state */
1134926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1135926deccbSFrançois Tigeot 	rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
1136926deccbSFrançois Tigeot 	rdev->pm.current_clock_mode_index = 0;
1137926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->pm.default_sclk;
1138926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->pm.default_mclk;
1139c6f73aabSFrançois Tigeot 	if (rdev->pm.power_state) {
1140926deccbSFrançois Tigeot 		rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
1141926deccbSFrançois Tigeot 		rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
1142c6f73aabSFrançois Tigeot 	}
1143926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM
1144926deccbSFrançois Tigeot 	    && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
1145926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1146926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1147926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1148926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1149926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1150926deccbSFrançois Tigeot 	}
1151926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1152926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1153926deccbSFrançois Tigeot }
1154926deccbSFrançois Tigeot 
115557e252bfSMichael Neumann static void radeon_pm_resume_dpm(struct radeon_device *rdev)
1156926deccbSFrançois Tigeot {
1157926deccbSFrançois Tigeot 	int ret;
1158926deccbSFrançois Tigeot 
115957e252bfSMichael Neumann 	/* asic init will reset to the boot state */
116057e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
116157e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
116257e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
116357e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
116457e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1165c6f73aabSFrançois Tigeot 	if (ret)
1166c6f73aabSFrançois Tigeot 		goto dpm_resume_fail;
1167c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1168c6f73aabSFrançois Tigeot 	return;
1169c6f73aabSFrançois Tigeot 
1170c6f73aabSFrançois Tigeot dpm_resume_fail:
117157e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm resume failed\n");
117257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
11734cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
117457e252bfSMichael Neumann 	    rdev->mc_fw) {
117557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
117657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
117757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
117857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
117957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
118057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
118157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
118257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
118357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
118457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
118557e252bfSMichael Neumann 	}
118657e252bfSMichael Neumann }
118757e252bfSMichael Neumann 
118857e252bfSMichael Neumann void radeon_pm_resume(struct radeon_device *rdev)
118957e252bfSMichael Neumann {
119057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
119157e252bfSMichael Neumann 		radeon_pm_resume_dpm(rdev);
119257e252bfSMichael Neumann 	else
119357e252bfSMichael Neumann 		radeon_pm_resume_old(rdev);
119457e252bfSMichael Neumann }
119557e252bfSMichael Neumann 
119657e252bfSMichael Neumann static int radeon_pm_init_old(struct radeon_device *rdev)
119757e252bfSMichael Neumann {
119857e252bfSMichael Neumann 	int ret;
119957e252bfSMichael Neumann 
1200926deccbSFrançois Tigeot 	rdev->pm.profile = PM_PROFILE_DEFAULT;
1201926deccbSFrançois Tigeot 	rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1202926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1203926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_upclock = true;
1204926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_downclock = true;
1205926deccbSFrançois Tigeot 	rdev->pm.default_sclk = rdev->clock.default_sclk;
1206926deccbSFrançois Tigeot 	rdev->pm.default_mclk = rdev->clock.default_mclk;
1207926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->clock.default_sclk;
1208926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->clock.default_mclk;
1209926deccbSFrançois Tigeot 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
1210926deccbSFrançois Tigeot 
1211926deccbSFrançois Tigeot 	if (rdev->bios) {
1212926deccbSFrançois Tigeot 		if (rdev->is_atom_bios)
1213926deccbSFrançois Tigeot 			radeon_atombios_get_power_modes(rdev);
1214926deccbSFrançois Tigeot 		else
1215926deccbSFrançois Tigeot 			radeon_combios_get_power_modes(rdev);
1216926deccbSFrançois Tigeot 		radeon_pm_print_states(rdev);
1217926deccbSFrançois Tigeot 		radeon_pm_init_profile(rdev);
1218926deccbSFrançois Tigeot 		/* set up the default clocks if the MC ucode is loaded */
1219926deccbSFrançois Tigeot 		if ((rdev->family >= CHIP_BARTS) &&
12204cd92098Szrj 		    (rdev->family <= CHIP_CAYMAN) &&
1221926deccbSFrançois Tigeot 		    rdev->mc_fw) {
1222926deccbSFrançois Tigeot 			if (rdev->pm.default_vddc)
1223926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1224926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDC);
1225926deccbSFrançois Tigeot 			if (rdev->pm.default_vddci)
1226926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1227926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDCI);
1228926deccbSFrançois Tigeot 			if (rdev->pm.default_sclk)
1229926deccbSFrançois Tigeot 				radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1230926deccbSFrançois Tigeot 			if (rdev->pm.default_mclk)
1231926deccbSFrançois Tigeot 				radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1232926deccbSFrançois Tigeot 		}
1233926deccbSFrançois Tigeot 	}
1234926deccbSFrançois Tigeot 
1235926deccbSFrançois Tigeot 	/* set up the internal thermal sensor if applicable */
1236926deccbSFrançois Tigeot 	ret = radeon_hwmon_init(rdev);
1237926deccbSFrançois Tigeot 	if (ret)
1238926deccbSFrançois Tigeot 		return ret;
1239926deccbSFrançois Tigeot 
1240926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1241926deccbSFrançois Tigeot 	INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler);
1242926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1243926deccbSFrançois Tigeot 
1244926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1245926deccbSFrançois Tigeot 		/* where's the best place to put these? */
1246926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1247926deccbSFrançois Tigeot 		ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1248926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1249926deccbSFrançois Tigeot 		if (ret)
1250926deccbSFrançois Tigeot 			DRM_ERROR("failed to create device file for power profile\n");
1251926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1252926deccbSFrançois Tigeot 		ret = device_create_file(rdev->dev, &dev_attr_power_method);
1253926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1254926deccbSFrançois Tigeot 		if (ret)
1255926deccbSFrançois Tigeot 			DRM_ERROR("failed to create device file for power method\n");
1256926deccbSFrançois Tigeot 
1257926deccbSFrançois Tigeot 		if (radeon_debugfs_pm_init(rdev)) {
1258926deccbSFrançois Tigeot 			DRM_ERROR("Failed to register debugfs file for PM!\n");
1259926deccbSFrançois Tigeot 		}
1260926deccbSFrançois Tigeot 
1261926deccbSFrançois Tigeot 		DRM_INFO("radeon: power management initialized\n");
1262926deccbSFrançois Tigeot 	}
1263926deccbSFrançois Tigeot 
1264926deccbSFrançois Tigeot 	return 0;
1265926deccbSFrançois Tigeot }
1266926deccbSFrançois Tigeot 
126757e252bfSMichael Neumann static void radeon_dpm_print_power_states(struct radeon_device *rdev)
126857e252bfSMichael Neumann {
126957e252bfSMichael Neumann 	int i;
127057e252bfSMichael Neumann 
127157e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
127257e252bfSMichael Neumann 		printk("== power state %d ==\n", i);
127357e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
127457e252bfSMichael Neumann 	}
127557e252bfSMichael Neumann }
127657e252bfSMichael Neumann 
127757e252bfSMichael Neumann static int radeon_pm_init_dpm(struct radeon_device *rdev)
127857e252bfSMichael Neumann {
127957e252bfSMichael Neumann 	int ret;
128057e252bfSMichael Neumann 
12814cd92098Szrj 	/* default to balanced state */
128257e252bfSMichael Neumann 	rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
128357e252bfSMichael Neumann 	rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
12844cd92098Szrj 	rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
128557e252bfSMichael Neumann 	rdev->pm.default_sclk = rdev->clock.default_sclk;
128657e252bfSMichael Neumann 	rdev->pm.default_mclk = rdev->clock.default_mclk;
128757e252bfSMichael Neumann 	rdev->pm.current_sclk = rdev->clock.default_sclk;
128857e252bfSMichael Neumann 	rdev->pm.current_mclk = rdev->clock.default_mclk;
128957e252bfSMichael Neumann 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
129057e252bfSMichael Neumann 
129157e252bfSMichael Neumann 	if (rdev->bios && rdev->is_atom_bios)
129257e252bfSMichael Neumann 		radeon_atombios_get_power_modes(rdev);
129357e252bfSMichael Neumann 	else
129457e252bfSMichael Neumann 		return -EINVAL;
129557e252bfSMichael Neumann 
129657e252bfSMichael Neumann 	/* set up the internal thermal sensor if applicable */
129757e252bfSMichael Neumann 	ret = radeon_hwmon_init(rdev);
129857e252bfSMichael Neumann 	if (ret)
129957e252bfSMichael Neumann 		return ret;
130057e252bfSMichael Neumann 
130157e252bfSMichael Neumann 	TASK_INIT(&rdev->pm.dpm.thermal.work, 0, radeon_dpm_thermal_work_handler, rdev);
130257e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
130357e252bfSMichael Neumann 	radeon_dpm_init(rdev);
130457e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
1305c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1)
130657e252bfSMichael Neumann 		radeon_dpm_print_power_states(rdev);
130757e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
130857e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
130957e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1310c6f73aabSFrançois Tigeot 	if (ret)
1311c6f73aabSFrançois Tigeot 		goto dpm_failed;
1312c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1313c6f73aabSFrançois Tigeot 
1314c6f73aabSFrançois Tigeot #ifdef TODO_DEVICE_FILE
1315c6f73aabSFrançois Tigeot 	ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
1316c6f73aabSFrançois Tigeot 	if (ret)
1317c6f73aabSFrançois Tigeot 		DRM_ERROR("failed to create device file for dpm state\n");
1318c6f73aabSFrançois Tigeot 	ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
1319c6f73aabSFrançois Tigeot 	if (ret)
1320c6f73aabSFrançois Tigeot 		DRM_ERROR("failed to create device file for dpm state\n");
1321c6f73aabSFrançois Tigeot 	/* XXX: these are noops for dpm but are here for backwards compat */
1322c6f73aabSFrançois Tigeot 	ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1323c6f73aabSFrançois Tigeot 	if (ret)
1324c6f73aabSFrançois Tigeot 		DRM_ERROR("failed to create device file for power profile\n");
1325c6f73aabSFrançois Tigeot 	ret = device_create_file(rdev->dev, &dev_attr_power_method);
1326c6f73aabSFrançois Tigeot 	if (ret)
1327c6f73aabSFrançois Tigeot 		DRM_ERROR("failed to create device file for power method\n");
1328c6f73aabSFrançois Tigeot 
1329c6f73aabSFrançois Tigeot 	if (radeon_debugfs_pm_init(rdev)) {
1330c6f73aabSFrançois Tigeot 		DRM_ERROR("Failed to register debugfs file for dpm!\n");
1331c6f73aabSFrançois Tigeot 	}
1332c6f73aabSFrançois Tigeot #endif
1333c6f73aabSFrançois Tigeot 
1334c6f73aabSFrançois Tigeot 	DRM_INFO("radeon: dpm initialized\n");
1335c6f73aabSFrançois Tigeot 
1336c6f73aabSFrançois Tigeot 	return 0;
1337c6f73aabSFrançois Tigeot 
1338c6f73aabSFrançois Tigeot dpm_failed:
133957e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
134057e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
13414cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
134257e252bfSMichael Neumann 	    rdev->mc_fw) {
134357e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
134457e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
134557e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
134657e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
134757e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
134857e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
134957e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
135057e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
135157e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
135257e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
135357e252bfSMichael Neumann 	}
135457e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm initialization failed\n");
135557e252bfSMichael Neumann 	return ret;
135657e252bfSMichael Neumann }
135757e252bfSMichael Neumann 
135857e252bfSMichael Neumann int radeon_pm_init(struct radeon_device *rdev)
135957e252bfSMichael Neumann {
136057e252bfSMichael Neumann 	/* enable dpm on rv6xx+ */
136157e252bfSMichael Neumann 	switch (rdev->family) {
136257e252bfSMichael Neumann 	case CHIP_RV610:
136357e252bfSMichael Neumann 	case CHIP_RV630:
136457e252bfSMichael Neumann 	case CHIP_RV620:
136557e252bfSMichael Neumann 	case CHIP_RV635:
136657e252bfSMichael Neumann 	case CHIP_RV670:
136757e252bfSMichael Neumann 	case CHIP_RS780:
136857e252bfSMichael Neumann 	case CHIP_RS880:
136957e252bfSMichael Neumann 	case CHIP_RV770:
1370c6f73aabSFrançois Tigeot 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
1371c6f73aabSFrançois Tigeot 		if (!rdev->rlc_fw)
1372c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1373c6f73aabSFrançois Tigeot 		else if ((rdev->family >= CHIP_RV770) &&
1374c6f73aabSFrançois Tigeot 			 (!(rdev->flags & RADEON_IS_IGP)) &&
1375c6f73aabSFrançois Tigeot 			 (!rdev->smc_fw))
1376c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1377c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 1)
1378c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
1379c6f73aabSFrançois Tigeot 		else
1380c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1381c6f73aabSFrançois Tigeot 		break;
138257e252bfSMichael Neumann 	case CHIP_RV730:
138357e252bfSMichael Neumann 	case CHIP_RV710:
138457e252bfSMichael Neumann 	case CHIP_RV740:
138557e252bfSMichael Neumann 	case CHIP_CEDAR:
138657e252bfSMichael Neumann 	case CHIP_REDWOOD:
138757e252bfSMichael Neumann 	case CHIP_JUNIPER:
138857e252bfSMichael Neumann 	case CHIP_CYPRESS:
138957e252bfSMichael Neumann 	case CHIP_HEMLOCK:
139057e252bfSMichael Neumann 	case CHIP_PALM:
139157e252bfSMichael Neumann 	case CHIP_SUMO:
139257e252bfSMichael Neumann 	case CHIP_SUMO2:
139357e252bfSMichael Neumann 	case CHIP_BARTS:
139457e252bfSMichael Neumann 	case CHIP_TURKS:
139557e252bfSMichael Neumann 	case CHIP_CAICOS:
139657e252bfSMichael Neumann 	case CHIP_CAYMAN:
139757e252bfSMichael Neumann 	case CHIP_ARUBA:
139857e252bfSMichael Neumann 	case CHIP_TAHITI:
139957e252bfSMichael Neumann 	case CHIP_PITCAIRN:
140057e252bfSMichael Neumann 	case CHIP_VERDE:
140157e252bfSMichael Neumann 	case CHIP_OLAND:
140257e252bfSMichael Neumann 	case CHIP_HAINAN:
14034cd92098Szrj 	case CHIP_BONAIRE:
14044cd92098Szrj 	case CHIP_KABINI:
14054cd92098Szrj 	case CHIP_KAVERI:
1406c6f73aabSFrançois Tigeot 	case CHIP_HAWAII:
1407c6f73aabSFrançois Tigeot 	case CHIP_MULLINS:
140857e252bfSMichael Neumann 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
140957e252bfSMichael Neumann 		if (!rdev->rlc_fw)
141057e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
141157e252bfSMichael Neumann 		else if ((rdev->family >= CHIP_RV770) &&
141257e252bfSMichael Neumann 			 (!(rdev->flags & RADEON_IS_IGP)) &&
141357e252bfSMichael Neumann 			 (!rdev->smc_fw))
141457e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1415c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 0)
141657e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1417c6f73aabSFrançois Tigeot 		else
1418c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
141957e252bfSMichael Neumann 		break;
142057e252bfSMichael Neumann 	default:
142157e252bfSMichael Neumann 		/* default to profile method */
142257e252bfSMichael Neumann 		rdev->pm.pm_method = PM_METHOD_PROFILE;
142357e252bfSMichael Neumann 		break;
142457e252bfSMichael Neumann 	}
142557e252bfSMichael Neumann 
142657e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
142757e252bfSMichael Neumann 		return radeon_pm_init_dpm(rdev);
142857e252bfSMichael Neumann 	else
142957e252bfSMichael Neumann 		return radeon_pm_init_old(rdev);
143057e252bfSMichael Neumann }
143157e252bfSMichael Neumann 
1432c6f73aabSFrançois Tigeot int radeon_pm_late_init(struct radeon_device *rdev)
1433c6f73aabSFrançois Tigeot {
1434c6f73aabSFrançois Tigeot 	int ret = 0;
1435c6f73aabSFrançois Tigeot 
1436c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
1437c6f73aabSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1438c6f73aabSFrançois Tigeot 		ret = radeon_dpm_late_enable(rdev);
1439c6f73aabSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
1440c6f73aabSFrançois Tigeot 	}
1441c6f73aabSFrançois Tigeot 	return ret;
1442c6f73aabSFrançois Tigeot }
1443c6f73aabSFrançois Tigeot 
144457e252bfSMichael Neumann static void radeon_pm_fini_old(struct radeon_device *rdev)
1445926deccbSFrançois Tigeot {
1446926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1447926deccbSFrançois Tigeot 		DRM_UNLOCK(rdev->ddev); /* Work around LOR. */
1448926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1449926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1450926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
1451926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
1452926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1453926deccbSFrançois Tigeot 		} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1454926deccbSFrançois Tigeot 			/* reset default clocks */
1455926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1456926deccbSFrançois Tigeot 			rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1457926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1458926deccbSFrançois Tigeot 		}
1459926deccbSFrançois Tigeot 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
1460926deccbSFrançois Tigeot 		DRM_LOCK(rdev->ddev);
1461926deccbSFrançois Tigeot 
1462926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1463926deccbSFrançois Tigeot 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1464926deccbSFrançois Tigeot 
1465926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_profile);
1466926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_method);
1467926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1468926deccbSFrançois Tigeot 	}
1469926deccbSFrançois Tigeot 
1470926deccbSFrançois Tigeot 	if (rdev->pm.power_state) {
1471926deccbSFrançois Tigeot 		int i;
1472926deccbSFrançois Tigeot 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
1473c4ef309bSzrj 			kfree(rdev->pm.power_state[i].clock_info);
1474926deccbSFrançois Tigeot 		}
1475c4ef309bSzrj 		kfree(rdev->pm.power_state);
1476926deccbSFrançois Tigeot 		rdev->pm.power_state = NULL;
1477926deccbSFrançois Tigeot 		rdev->pm.num_power_states = 0;
1478926deccbSFrançois Tigeot 	}
1479926deccbSFrançois Tigeot 
1480926deccbSFrançois Tigeot 	radeon_hwmon_fini(rdev);
1481926deccbSFrançois Tigeot }
1482926deccbSFrançois Tigeot 
148357e252bfSMichael Neumann static void radeon_pm_fini_dpm(struct radeon_device *rdev)
148457e252bfSMichael Neumann {
148557e252bfSMichael Neumann 	if (rdev->pm.num_power_states > 1) {
148657e252bfSMichael Neumann 		lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
148757e252bfSMichael Neumann 		radeon_dpm_disable(rdev);
148857e252bfSMichael Neumann 		lockmgr(&rdev->pm.mutex, LK_RELEASE);
148957e252bfSMichael Neumann 
149057e252bfSMichael Neumann #ifdef TODO_DEVICE_FILE
149157e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
149257e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
149357e252bfSMichael Neumann 		/* XXX backwards compat */
149457e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_profile);
149557e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_method);
149657e252bfSMichael Neumann #endif
149757e252bfSMichael Neumann 	}
149857e252bfSMichael Neumann 	radeon_dpm_fini(rdev);
149957e252bfSMichael Neumann 
150057e252bfSMichael Neumann 	kfree(rdev->pm.power_state);
150157e252bfSMichael Neumann 
150257e252bfSMichael Neumann 	radeon_hwmon_fini(rdev);
150357e252bfSMichael Neumann }
150457e252bfSMichael Neumann 
150557e252bfSMichael Neumann void radeon_pm_fini(struct radeon_device *rdev)
150657e252bfSMichael Neumann {
150757e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
150857e252bfSMichael Neumann 		radeon_pm_fini_dpm(rdev);
150957e252bfSMichael Neumann 	else
151057e252bfSMichael Neumann 		radeon_pm_fini_old(rdev);
151157e252bfSMichael Neumann }
151257e252bfSMichael Neumann 
151357e252bfSMichael Neumann static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
1514926deccbSFrançois Tigeot {
1515926deccbSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1516926deccbSFrançois Tigeot 	struct drm_crtc *crtc;
1517926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc;
1518926deccbSFrançois Tigeot 
1519926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states < 2)
1520926deccbSFrançois Tigeot 		return;
1521926deccbSFrançois Tigeot 
1522926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1523926deccbSFrançois Tigeot 
1524926deccbSFrançois Tigeot 	rdev->pm.active_crtcs = 0;
1525926deccbSFrançois Tigeot 	rdev->pm.active_crtc_count = 0;
1526c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
1527926deccbSFrançois Tigeot 		list_for_each_entry(crtc,
1528926deccbSFrançois Tigeot 				    &ddev->mode_config.crtc_list, head) {
1529926deccbSFrançois Tigeot 			radeon_crtc = to_radeon_crtc(crtc);
1530926deccbSFrançois Tigeot 			if (radeon_crtc->enabled) {
1531926deccbSFrançois Tigeot 				rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
1532926deccbSFrançois Tigeot 				rdev->pm.active_crtc_count++;
1533926deccbSFrançois Tigeot 			}
1534926deccbSFrançois Tigeot 		}
1535c6f73aabSFrançois Tigeot 	}
1536926deccbSFrançois Tigeot 
1537926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1538926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
1539926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
1540926deccbSFrançois Tigeot 	} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1541926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) {
1542926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1) {
1543926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1544926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1545926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1546926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1547926deccbSFrançois Tigeot 
1548926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
1549926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1550926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1551926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1552926deccbSFrançois Tigeot 
1553926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n");
1554926deccbSFrançois Tigeot 				}
1555926deccbSFrançois Tigeot 			} else if (rdev->pm.active_crtc_count == 1) {
1556926deccbSFrançois Tigeot 				/* TODO: Increase clocks if needed for current mode */
1557926deccbSFrançois Tigeot 
1558926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) {
1559926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1560926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK;
1561926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1562926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1563926deccbSFrançois Tigeot 
1564926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1565926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1566926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1567926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1568926deccbSFrançois Tigeot 				} else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) {
1569926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1570926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1571926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1572926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1573926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1574926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n");
1575926deccbSFrançois Tigeot 				}
1576926deccbSFrançois Tigeot 			} else { /* count == 0 */
1577926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) {
1578926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1579926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1580926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1581926deccbSFrançois Tigeot 
1582926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM;
1583926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM;
1584926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1585926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1586926deccbSFrançois Tigeot 				}
1587926deccbSFrançois Tigeot 			}
1588926deccbSFrançois Tigeot 		}
1589926deccbSFrançois Tigeot 	}
1590926deccbSFrançois Tigeot 
1591926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1592926deccbSFrançois Tigeot }
1593926deccbSFrançois Tigeot 
159457e252bfSMichael Neumann static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
159557e252bfSMichael Neumann {
159657e252bfSMichael Neumann 	struct drm_device *ddev = rdev->ddev;
159757e252bfSMichael Neumann 	struct drm_crtc *crtc;
159857e252bfSMichael Neumann 	struct radeon_crtc *radeon_crtc;
159957e252bfSMichael Neumann 
1600c6f73aabSFrançois Tigeot 	if (!rdev->pm.dpm_enabled)
1601c6f73aabSFrançois Tigeot 		return;
1602c6f73aabSFrançois Tigeot 
160357e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
160457e252bfSMichael Neumann 
160557e252bfSMichael Neumann 	/* update active crtc counts */
160657e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtcs = 0;
160757e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtc_count = 0;
1608c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
160957e252bfSMichael Neumann 		list_for_each_entry(crtc,
161057e252bfSMichael Neumann 				    &ddev->mode_config.crtc_list, head) {
161157e252bfSMichael Neumann 			radeon_crtc = to_radeon_crtc(crtc);
161257e252bfSMichael Neumann 			if (crtc->enabled) {
161357e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
161457e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtc_count++;
161557e252bfSMichael Neumann 			}
161657e252bfSMichael Neumann 		}
1617c6f73aabSFrançois Tigeot 	}
161857e252bfSMichael Neumann 
161957e252bfSMichael Neumann 	/* update battery/ac status */
162057e252bfSMichael Neumann 	if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
162157e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = true;
162257e252bfSMichael Neumann 	else
162357e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = false;
162457e252bfSMichael Neumann 
162557e252bfSMichael Neumann 	radeon_dpm_change_power_state_locked(rdev);
162657e252bfSMichael Neumann 
162757e252bfSMichael Neumann 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1628c6f73aabSFrançois Tigeot 
162957e252bfSMichael Neumann }
163057e252bfSMichael Neumann 
163157e252bfSMichael Neumann void radeon_pm_compute_clocks(struct radeon_device *rdev)
163257e252bfSMichael Neumann {
163357e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
163457e252bfSMichael Neumann 		radeon_pm_compute_clocks_dpm(rdev);
163557e252bfSMichael Neumann 	else
163657e252bfSMichael Neumann 		radeon_pm_compute_clocks_old(rdev);
163757e252bfSMichael Neumann }
163857e252bfSMichael Neumann 
1639926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev)
1640926deccbSFrançois Tigeot {
1641926deccbSFrançois Tigeot 	int  crtc, vpos, hpos, vbl_status;
1642926deccbSFrançois Tigeot 	bool in_vbl = true;
1643926deccbSFrançois Tigeot 
1644926deccbSFrançois Tigeot 	/* Iterate over all active crtc's. All crtc's must be in vblank,
1645926deccbSFrançois Tigeot 	 * otherwise return in_vbl == false.
1646926deccbSFrançois Tigeot 	 */
1647926deccbSFrançois Tigeot 	for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
1648926deccbSFrançois Tigeot 		if (rdev->pm.active_crtcs & (1 << crtc)) {
1649782e40d3SFrançois Tigeot 			vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL);
1650926deccbSFrançois Tigeot 			if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
1651926deccbSFrançois Tigeot 			    !(vbl_status & DRM_SCANOUTPOS_INVBL))
1652926deccbSFrançois Tigeot 				in_vbl = false;
1653926deccbSFrançois Tigeot 		}
1654926deccbSFrançois Tigeot 	}
1655926deccbSFrançois Tigeot 
1656926deccbSFrançois Tigeot 	return in_vbl;
1657926deccbSFrançois Tigeot }
1658926deccbSFrançois Tigeot 
1659926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
1660926deccbSFrançois Tigeot {
1661926deccbSFrançois Tigeot 	u32 stat_crtc = 0;
1662926deccbSFrançois Tigeot 	bool in_vbl = radeon_pm_in_vbl(rdev);
1663926deccbSFrançois Tigeot 
1664926deccbSFrançois Tigeot 	if (in_vbl == false)
1665926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc,
1666926deccbSFrançois Tigeot 			 finish ? "exit" : "entry");
1667926deccbSFrançois Tigeot 	return in_vbl;
1668926deccbSFrançois Tigeot }
1669926deccbSFrançois Tigeot 
1670926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1671926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work)
1672926deccbSFrançois Tigeot {
1673926deccbSFrançois Tigeot 	struct radeon_device *rdev;
1674926deccbSFrançois Tigeot 	int resched;
1675926deccbSFrançois Tigeot 	rdev = container_of(work, struct radeon_device,
1676926deccbSFrançois Tigeot 				pm.dynpm_idle_work.work);
1677926deccbSFrançois Tigeot 
1678926deccbSFrançois Tigeot 	resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
1679926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_EXCLUSIVE);
1680926deccbSFrançois Tigeot 	if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1681926deccbSFrançois Tigeot 		int not_processed = 0;
1682926deccbSFrançois Tigeot 		int i;
1683926deccbSFrançois Tigeot 
1684926deccbSFrançois Tigeot 		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
1685926deccbSFrançois Tigeot 			struct radeon_ring *ring = &rdev->ring[i];
1686926deccbSFrançois Tigeot 
1687926deccbSFrançois Tigeot 			if (ring->ready) {
1688926deccbSFrançois Tigeot 				not_processed += radeon_fence_count_emitted(rdev, i);
1689926deccbSFrançois Tigeot 				if (not_processed >= 3)
1690926deccbSFrançois Tigeot 					break;
1691926deccbSFrançois Tigeot 			}
1692926deccbSFrançois Tigeot 		}
1693926deccbSFrançois Tigeot 
1694926deccbSFrançois Tigeot 		if (not_processed >= 3) { /* should upclock */
1695926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) {
1696926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1697926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1698926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_upclock) {
1699926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1700926deccbSFrançois Tigeot 					DYNPM_ACTION_UPCLOCK;
1701926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1702926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1703926deccbSFrançois Tigeot 			}
1704926deccbSFrançois Tigeot 		} else if (not_processed == 0) { /* should downclock */
1705926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) {
1706926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1707926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1708926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_downclock) {
1709926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1710926deccbSFrançois Tigeot 					DYNPM_ACTION_DOWNCLOCK;
1711926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1712926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1713926deccbSFrançois Tigeot 			}
1714926deccbSFrançois Tigeot 		}
1715926deccbSFrançois Tigeot 
1716926deccbSFrançois Tigeot 		/* Note, radeon_pm_set_clocks is called with static_switch set
1717926deccbSFrançois Tigeot 		 * to false since we want to wait for vbl to avoid flicker.
1718926deccbSFrançois Tigeot 		 */
1719926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE &&
1720926deccbSFrançois Tigeot 		    jiffies > rdev->pm.dynpm_action_timeout) {
1721926deccbSFrançois Tigeot 			radeon_pm_get_dynpm_state(rdev);
1722926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1723926deccbSFrançois Tigeot 		}
1724926deccbSFrançois Tigeot 
1725926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1726926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1727926deccbSFrançois Tigeot 	}
1728926deccbSFrançois Tigeot 	lockmgr(&rdev->pm.mutex, LK_RELEASE);
1729926deccbSFrançois Tigeot 	ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
1730926deccbSFrançois Tigeot }
1731926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1732926deccbSFrançois Tigeot 
1733926deccbSFrançois Tigeot /*
1734926deccbSFrançois Tigeot  * Debugfs info
1735926deccbSFrançois Tigeot  */
1736926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1737926deccbSFrançois Tigeot 
1738926deccbSFrançois Tigeot static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
1739926deccbSFrançois Tigeot {
1740926deccbSFrançois Tigeot 	struct drm_info_node *node = (struct drm_info_node *) m->private;
1741926deccbSFrançois Tigeot 	struct drm_device *dev = node->minor->dev;
1742926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
1743c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1744926deccbSFrançois Tigeot 
1745c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
1746c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
1747c6f73aabSFrançois Tigeot 		seq_printf(m, "PX asic powered off\n");
1748c6f73aabSFrançois Tigeot 	} else if (rdev->pm.dpm_enabled) {
174957e252bfSMichael Neumann 		spin_lock(&rdev->pm.mutex);
175057e252bfSMichael Neumann 		if (rdev->asic->dpm.debugfs_print_current_performance_level)
175157e252bfSMichael Neumann 			radeon_dpm_debugfs_print_current_performance_level(rdev, m);
175257e252bfSMichael Neumann 		else
175357e252bfSMichael Neumann 			seq_printf(m, "Debugfs support not implemented for this asic\n");
175457e252bfSMichael Neumann 		spin_unlock(&rdev->pm.mutex);
175557e252bfSMichael Neumann 	} else {
1756926deccbSFrançois Tigeot 		seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
1757f43cf1b1SMichael Neumann 		/* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
1758f43cf1b1SMichael Neumann 		if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
1759f43cf1b1SMichael Neumann 			seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
1760f43cf1b1SMichael Neumann 		else
1761926deccbSFrançois Tigeot 			seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
1762926deccbSFrançois Tigeot 		seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
1763926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_memory_clock)
1764926deccbSFrançois Tigeot 			seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
1765926deccbSFrançois Tigeot 		if (rdev->pm.current_vddc)
1766926deccbSFrançois Tigeot 			seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
1767926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_pcie_lanes)
1768926deccbSFrançois Tigeot 			seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
176957e252bfSMichael Neumann 	}
1770926deccbSFrançois Tigeot 
1771926deccbSFrançois Tigeot 	return 0;
1772926deccbSFrançois Tigeot }
1773926deccbSFrançois Tigeot 
1774926deccbSFrançois Tigeot static struct drm_info_list radeon_pm_info_list[] = {
1775926deccbSFrançois Tigeot 	{"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL},
1776926deccbSFrançois Tigeot };
1777926deccbSFrançois Tigeot #endif
1778926deccbSFrançois Tigeot 
1779926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev)
1780926deccbSFrançois Tigeot {
1781926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1782926deccbSFrançois Tigeot 	return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
1783926deccbSFrançois Tigeot #else
1784926deccbSFrançois Tigeot 	return 0;
1785926deccbSFrançois Tigeot #endif
1786926deccbSFrançois Tigeot }
1787