xref: /dflybsd-src/sys/dev/drm/radeon/radeon_pm.c (revision 1cfef1a587a371344cf93e80367482432b0933a2)
1926deccbSFrançois Tigeot /*
2926deccbSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
3926deccbSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
4926deccbSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
5926deccbSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6926deccbSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
7926deccbSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
8926deccbSFrançois Tigeot  *
9926deccbSFrançois Tigeot  * The above copyright notice and this permission notice shall be included in
10926deccbSFrançois Tigeot  * all copies or substantial portions of the Software.
11926deccbSFrançois Tigeot  *
12926deccbSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13926deccbSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14926deccbSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
15926deccbSFrançois Tigeot  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
16926deccbSFrançois Tigeot  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
17926deccbSFrançois Tigeot  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
18926deccbSFrançois Tigeot  * OTHER DEALINGS IN THE SOFTWARE.
19926deccbSFrançois Tigeot  *
20926deccbSFrançois Tigeot  * Authors: Rafał Miłecki <zajec5@gmail.com>
21926deccbSFrançois Tigeot  *          Alex Deucher <alexdeucher@gmail.com>
22926deccbSFrançois Tigeot  */
23926deccbSFrançois Tigeot #include <drm/drmP.h>
24926deccbSFrançois Tigeot #include "radeon.h"
25926deccbSFrançois Tigeot #include "avivod.h"
26926deccbSFrançois Tigeot #include "atom.h"
27*1cfef1a5SFrançois Tigeot #include <linux/power_supply.h>
28*1cfef1a5SFrançois Tigeot #include <linux/hwmon.h>
29*1cfef1a5SFrançois Tigeot 
30*1cfef1a5SFrançois Tigeot #include <sys/power.h>
31*1cfef1a5SFrançois Tigeot #include <sys/sensors.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 
45ee479021SImre Vadász #ifdef DUMBBELL_WIP
46926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work);
47ee479021SImre Vadász #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) {
75*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
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 		}
84*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
854cd92098Szrj         } else if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
86926deccbSFrançois Tigeot 		if (rdev->pm.profile == PM_PROFILE_AUTO) {
87*1cfef1a5SFrançois Tigeot 			mutex_lock(&rdev->pm.mutex);
88926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
89926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
90*1cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->pm.mutex);
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;
164ee479021SImre Vadász #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));
168ee479021SImre Vadász #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 
261*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ddev->struct_mutex);
262*1cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
263*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
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 */
274*1cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->ring_lock);
275*1cfef1a5SFrançois Tigeot 			up_write(&rdev->pm.mclk_lock);
276*1cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->ddev->struct_mutex);
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);
287ee479021SImre Vadász 				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 
310*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
311*1cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
312*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ddev->struct_mutex);
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 
378*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
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:
400*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
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) {
440*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
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;
444*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
445926deccbSFrançois Tigeot 	} else if (strncmp("profile", buf, strlen("profile")) == 0) {
446*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
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;
451*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
452ee479021SImre Vadász #ifdef DUMBBELL_WIP
453926deccbSFrançois Tigeot 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
454ee479021SImre Vadász #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 
485*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
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 {
493*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
49457e252bfSMichael Neumann 		count = -EINVAL;
49557e252bfSMichael Neumann 		goto fail;
49657e252bfSMichael Neumann 	}
497*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
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))
518*1cfef1a5SFrançois Tigeot 		return ksnprintf(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 
540*1cfef1a5SFrançois Tigeot 	mutex_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:
561*1cfef1a5SFrançois Tigeot 	mutex_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 
627ee479021SImre Vadász 	/* 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 ||
630ee479021SImre Vadász 	     attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr))
631c6f73aabSFrançois Tigeot 		return 0;
632c6f73aabSFrançois Tigeot 
633ee479021SImre Vadász 	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;
65126b5dbf2SImre Vadász 	struct drm_device *ddev = rdev->ddev;
6522c86cb5bSImre Vadász 	struct ksensor *s = rdev->pm.int_sensor;
6532c86cb5bSImre Vadász 	int temp;
65426b5dbf2SImre Vadász 	enum sensor_status stat;
6552c86cb5bSImre Vadász 
65626b5dbf2SImre Vadász 	/* Can't get temperature when the card is off */
65726b5dbf2SImre Vadász 	if  ((rdev->flags & RADEON_IS_PX) &&
65826b5dbf2SImre Vadász 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
65926b5dbf2SImre Vadász 		sensor_set_unknown(s);
66026b5dbf2SImre Vadász 		s->status = SENSOR_S_OK;
66126b5dbf2SImre Vadász 		return;
66226b5dbf2SImre Vadász 	}
66326b5dbf2SImre Vadász 
66426b5dbf2SImre Vadász 	if (rdev->asic->pm.get_temperature == NULL) {
66526b5dbf2SImre Vadász 		sensor_set_invalid(s);
66626b5dbf2SImre Vadász 		return;
66726b5dbf2SImre Vadász 	}
66826b5dbf2SImre Vadász 
6692c86cb5bSImre Vadász 	temp = radeon_get_temperature(rdev);
67026b5dbf2SImre Vadász 	if (temp >= rdev->pm.dpm.thermal.max_temp)
67126b5dbf2SImre Vadász 		stat = SENSOR_S_CRIT;
67226b5dbf2SImre Vadász 	else if (temp >= rdev->pm.dpm.thermal.min_temp)
67326b5dbf2SImre Vadász 		stat = SENSOR_S_WARN;
6742c86cb5bSImre Vadász 	else
67526b5dbf2SImre Vadász 		stat = SENSOR_S_OK;
6762c86cb5bSImre Vadász 
67726b5dbf2SImre 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,
705fb572d17SFrançois Tigeot 		    device_get_nameunit(rdev->dev->bsddev),
7062c86cb5bSImre Vadász 		    sizeof(rdev->pm.int_sensordev->xname));
7072c86cb5bSImre Vadász 		rdev->pm.int_sensor->type = SENSOR_TEMP;
70826b5dbf2SImre 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 
7322c5cc6b9SFrançois Tigeot static void radeon_dpm_thermal_work_handler(struct work_struct *work)
73357e252bfSMichael Neumann {
7342c5cc6b9SFrançois Tigeot 	struct radeon_device *rdev =
7352c5cc6b9SFrançois Tigeot 		container_of(work, struct radeon_device,
7362c5cc6b9SFrançois Tigeot 			     pm.dpm.thermal.work);
73757e252bfSMichael Neumann 	/* switch to the thermal state */
73857e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
73957e252bfSMichael Neumann 
74057e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
74157e252bfSMichael Neumann 		return;
74257e252bfSMichael Neumann 
74357e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature) {
74457e252bfSMichael Neumann 		int temp = radeon_get_temperature(rdev);
74557e252bfSMichael Neumann 
74657e252bfSMichael Neumann 		if (temp < rdev->pm.dpm.thermal.min_temp)
74757e252bfSMichael Neumann 			/* switch back the user state */
74857e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
74957e252bfSMichael Neumann 	} else {
75057e252bfSMichael Neumann 		if (rdev->pm.dpm.thermal.high_to_low)
75157e252bfSMichael Neumann 			/* switch back the user state */
75257e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
75357e252bfSMichael Neumann 	}
7544cd92098Szrj 	mutex_lock(&rdev->pm.mutex);
7554cd92098Szrj 	if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL)
7564cd92098Szrj 		rdev->pm.dpm.thermal_active = true;
7574cd92098Szrj 	else
7584cd92098Szrj 		rdev->pm.dpm.thermal_active = false;
7594cd92098Szrj 	rdev->pm.dpm.state = dpm_state;
7604cd92098Szrj 	mutex_unlock(&rdev->pm.mutex);
7614cd92098Szrj 
7624cd92098Szrj 	radeon_pm_compute_clocks(rdev);
76357e252bfSMichael Neumann }
76457e252bfSMichael Neumann 
765ee479021SImre Vadász static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
766ee479021SImre Vadász 						     enum radeon_pm_state_type dpm_state)
76757e252bfSMichael Neumann {
768ee479021SImre Vadász 	int i;
769ee479021SImre Vadász 	struct radeon_ps *ps;
770ee479021SImre Vadász 	u32 ui_class;
77157e252bfSMichael Neumann 	bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
77257e252bfSMichael Neumann 		true : false;
77357e252bfSMichael Neumann 
77457e252bfSMichael Neumann 	/* check if the vblank period is too short to adjust the mclk */
77557e252bfSMichael Neumann 	if (single_display && rdev->asic->dpm.vblank_too_short) {
77657e252bfSMichael Neumann 		if (radeon_dpm_vblank_too_short(rdev))
77757e252bfSMichael Neumann 			single_display = false;
77857e252bfSMichael Neumann 	}
77957e252bfSMichael Neumann 
78057e252bfSMichael Neumann 	/* certain older asics have a separare 3D performance state,
78157e252bfSMichael Neumann 	 * so try that first if the user selected performance
78257e252bfSMichael Neumann 	 */
78357e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
78457e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
78557e252bfSMichael Neumann 	/* balanced states don't exist at the moment */
78657e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_BALANCED)
78757e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
78857e252bfSMichael Neumann 
78957e252bfSMichael Neumann restart_search:
79057e252bfSMichael Neumann 	/* Pick the best power state based on current conditions */
79157e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
79257e252bfSMichael Neumann 		ps = &rdev->pm.dpm.ps[i];
79357e252bfSMichael Neumann 		ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
79457e252bfSMichael Neumann 		switch (dpm_state) {
79557e252bfSMichael Neumann 		/* user states */
79657e252bfSMichael Neumann 		case POWER_STATE_TYPE_BATTERY:
79757e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
79857e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
79957e252bfSMichael Neumann 					if (single_display)
80057e252bfSMichael Neumann 						return ps;
80157e252bfSMichael Neumann 				} else
80257e252bfSMichael Neumann 					return ps;
80357e252bfSMichael Neumann 			}
80457e252bfSMichael Neumann 			break;
80557e252bfSMichael Neumann 		case POWER_STATE_TYPE_BALANCED:
80657e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
80757e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
80857e252bfSMichael Neumann 					if (single_display)
80957e252bfSMichael Neumann 						return ps;
81057e252bfSMichael Neumann 				} else
81157e252bfSMichael Neumann 					return ps;
81257e252bfSMichael Neumann 			}
81357e252bfSMichael Neumann 			break;
81457e252bfSMichael Neumann 		case POWER_STATE_TYPE_PERFORMANCE:
81557e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
81657e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
81757e252bfSMichael Neumann 					if (single_display)
81857e252bfSMichael Neumann 						return ps;
81957e252bfSMichael Neumann 				} else
82057e252bfSMichael Neumann 					return ps;
82157e252bfSMichael Neumann 			}
82257e252bfSMichael Neumann 			break;
82357e252bfSMichael Neumann 		/* internal states */
82457e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD:
8254cd92098Szrj 			if (rdev->pm.dpm.uvd_ps)
82657e252bfSMichael Neumann 				return rdev->pm.dpm.uvd_ps;
8274cd92098Szrj 			else
8284cd92098Szrj 				break;
82957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_SD:
83057e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
83157e252bfSMichael Neumann 				return ps;
83257e252bfSMichael Neumann 			break;
83357e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD:
83457e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
83557e252bfSMichael Neumann 				return ps;
83657e252bfSMichael Neumann 			break;
83757e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
83857e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
83957e252bfSMichael Neumann 				return ps;
84057e252bfSMichael Neumann 			break;
84157e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
84257e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
84357e252bfSMichael Neumann 				return ps;
84457e252bfSMichael Neumann 			break;
84557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_BOOT:
84657e252bfSMichael Neumann 			return rdev->pm.dpm.boot_ps;
84757e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_THERMAL:
84857e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
84957e252bfSMichael Neumann 				return ps;
85057e252bfSMichael Neumann 			break;
85157e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ACPI:
85257e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
85357e252bfSMichael Neumann 				return ps;
85457e252bfSMichael Neumann 			break;
85557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ULV:
85657e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
85757e252bfSMichael Neumann 				return ps;
85857e252bfSMichael Neumann 			break;
85957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_3DPERF:
86057e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
86157e252bfSMichael Neumann 				return ps;
86257e252bfSMichael Neumann 			break;
86357e252bfSMichael Neumann 		default:
86457e252bfSMichael Neumann 			break;
86557e252bfSMichael Neumann 		}
86657e252bfSMichael Neumann 	}
86757e252bfSMichael Neumann 	/* use a fallback state if we didn't match */
86857e252bfSMichael Neumann 	switch (dpm_state) {
86957e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_SD:
8704cd92098Szrj 		dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
8714cd92098Szrj 		goto restart_search;
87257e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD:
87357e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
87457e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
8754cd92098Szrj 		if (rdev->pm.dpm.uvd_ps) {
87657e252bfSMichael Neumann 			return rdev->pm.dpm.uvd_ps;
8774cd92098Szrj 		} else {
8784cd92098Szrj 			dpm_state = POWER_STATE_TYPE_PERFORMANCE;
8794cd92098Szrj 			goto restart_search;
8804cd92098Szrj 		}
88157e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_THERMAL:
88257e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
88357e252bfSMichael Neumann 		goto restart_search;
88457e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_ACPI:
88557e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_BATTERY;
88657e252bfSMichael Neumann 		goto restart_search;
88757e252bfSMichael Neumann 	case POWER_STATE_TYPE_BATTERY:
88857e252bfSMichael Neumann 	case POWER_STATE_TYPE_BALANCED:
88957e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_3DPERF:
89057e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
89157e252bfSMichael Neumann 		goto restart_search;
89257e252bfSMichael Neumann 	default:
89357e252bfSMichael Neumann 		break;
89457e252bfSMichael Neumann 	}
89557e252bfSMichael Neumann 
89657e252bfSMichael Neumann 	return NULL;
89757e252bfSMichael Neumann }
89857e252bfSMichael Neumann 
89957e252bfSMichael Neumann static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
90057e252bfSMichael Neumann {
90157e252bfSMichael Neumann 	int i;
90257e252bfSMichael Neumann 	struct radeon_ps *ps;
90357e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state;
90457e252bfSMichael Neumann 	int ret;
90557e252bfSMichael Neumann 
90657e252bfSMichael Neumann 	/* if dpm init failed */
90757e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
90857e252bfSMichael Neumann 		return;
90957e252bfSMichael Neumann 
91057e252bfSMichael Neumann 	if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
91157e252bfSMichael Neumann 		/* add other state override checks here */
91257e252bfSMichael Neumann 		if ((!rdev->pm.dpm.thermal_active) &&
91357e252bfSMichael Neumann 		    (!rdev->pm.dpm.uvd_active))
91457e252bfSMichael Neumann 			rdev->pm.dpm.state = rdev->pm.dpm.user_state;
91557e252bfSMichael Neumann 	}
91657e252bfSMichael Neumann 	dpm_state = rdev->pm.dpm.state;
91757e252bfSMichael Neumann 
91857e252bfSMichael Neumann 	ps = radeon_dpm_pick_power_state(rdev, dpm_state);
91957e252bfSMichael Neumann 	if (ps)
92057e252bfSMichael Neumann 		rdev->pm.dpm.requested_ps = ps;
92157e252bfSMichael Neumann 	else
92257e252bfSMichael Neumann 		return;
92357e252bfSMichael Neumann 
92457e252bfSMichael Neumann 	/* no need to reprogram if nothing changed unless we are on BTC+ */
92557e252bfSMichael Neumann 	if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
926c6f73aabSFrançois Tigeot 		/* vce just modifies an existing state so force a change */
927c6f73aabSFrançois Tigeot 		if (ps->vce_active != rdev->pm.dpm.vce_active)
928c6f73aabSFrançois Tigeot 			goto force;
92957e252bfSMichael Neumann 		if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
93057e252bfSMichael Neumann 			/* for pre-BTC and APUs if the num crtcs changed but state is the same,
93157e252bfSMichael Neumann 			 * all we need to do is update the display configuration.
93257e252bfSMichael Neumann 			 */
93357e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
93457e252bfSMichael Neumann 				/* update display watermarks based on new power state */
93557e252bfSMichael Neumann 				radeon_bandwidth_update(rdev);
93657e252bfSMichael Neumann 				/* update displays */
93757e252bfSMichael Neumann 				radeon_dpm_display_configuration_changed(rdev);
93857e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
93957e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
94057e252bfSMichael Neumann 			}
94157e252bfSMichael Neumann 			return;
94257e252bfSMichael Neumann 		} else {
94357e252bfSMichael Neumann 			/* for BTC+ if the num crtcs hasn't changed and state is the same,
94457e252bfSMichael Neumann 			 * nothing to do, if the num crtcs is > 1 and state is the same,
94557e252bfSMichael Neumann 			 * update display configuration.
94657e252bfSMichael Neumann 			 */
94757e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs ==
94857e252bfSMichael Neumann 			    rdev->pm.dpm.current_active_crtcs) {
94957e252bfSMichael Neumann 				return;
95057e252bfSMichael Neumann 			} else {
95157e252bfSMichael Neumann 				if ((rdev->pm.dpm.current_active_crtc_count > 1) &&
95257e252bfSMichael Neumann 				    (rdev->pm.dpm.new_active_crtc_count > 1)) {
95357e252bfSMichael Neumann 					/* update display watermarks based on new power state */
95457e252bfSMichael Neumann 					radeon_bandwidth_update(rdev);
95557e252bfSMichael Neumann 					/* update displays */
95657e252bfSMichael Neumann 					radeon_dpm_display_configuration_changed(rdev);
95757e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
95857e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
95957e252bfSMichael Neumann 					return;
96057e252bfSMichael Neumann 				}
96157e252bfSMichael Neumann 			}
96257e252bfSMichael Neumann 		}
96357e252bfSMichael Neumann 	}
96457e252bfSMichael Neumann 
965c6f73aabSFrançois Tigeot force:
966c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1) {
96757e252bfSMichael Neumann 		printk("switching from power state:\n");
96857e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
96957e252bfSMichael Neumann 		printk("switching to power state:\n");
97057e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
971c6f73aabSFrançois Tigeot 	}
97257e252bfSMichael Neumann 
973*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ddev->struct_mutex);
974*1cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
975*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
97657e252bfSMichael Neumann 
977c6f73aabSFrançois Tigeot 	/* update whether vce is active */
978c6f73aabSFrançois Tigeot 	ps->vce_active = rdev->pm.dpm.vce_active;
979c6f73aabSFrançois Tigeot 
98057e252bfSMichael Neumann 	ret = radeon_dpm_pre_set_power_state(rdev);
98157e252bfSMichael Neumann 	if (ret)
98257e252bfSMichael Neumann 		goto done;
98357e252bfSMichael Neumann 
98457e252bfSMichael Neumann 	/* update display watermarks based on new power state */
98557e252bfSMichael Neumann 	radeon_bandwidth_update(rdev);
98657e252bfSMichael Neumann 	/* update displays */
98757e252bfSMichael Neumann 	radeon_dpm_display_configuration_changed(rdev);
98857e252bfSMichael Neumann 
989ee479021SImre Vadász 	rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
990ee479021SImre Vadász 	rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
991ee479021SImre Vadász 
99257e252bfSMichael Neumann 	/* wait for the rings to drain */
99357e252bfSMichael Neumann 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
99457e252bfSMichael Neumann 		struct radeon_ring *ring = &rdev->ring[i];
99557e252bfSMichael Neumann 		if (ring->ready)
996c6f73aabSFrançois Tigeot 			radeon_fence_wait_empty(rdev, i);
99757e252bfSMichael Neumann 	}
99857e252bfSMichael Neumann 
99957e252bfSMichael Neumann 	/* program the new power state */
100057e252bfSMichael Neumann 	radeon_dpm_set_power_state(rdev);
100157e252bfSMichael Neumann 
100257e252bfSMichael Neumann 	/* update current power state */
100357e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
100457e252bfSMichael Neumann 
100557e252bfSMichael Neumann 	radeon_dpm_post_set_power_state(rdev);
100657e252bfSMichael Neumann 
10074cd92098Szrj 	if (rdev->asic->dpm.force_performance_level) {
1008c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
1009c6f73aabSFrançois Tigeot 			enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
10104cd92098Szrj 			/* force low perf level for thermal */
10114cd92098Szrj 			radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW);
1012c6f73aabSFrançois Tigeot 			/* save the user's level */
1013c6f73aabSFrançois Tigeot 			rdev->pm.dpm.forced_level = level;
1014c6f73aabSFrançois Tigeot 		} else {
1015c6f73aabSFrançois Tigeot 			/* otherwise, user selected level */
1016c6f73aabSFrançois Tigeot 			radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level);
1017c6f73aabSFrançois Tigeot 		}
10184cd92098Szrj 	}
10194cd92098Szrj 
102057e252bfSMichael Neumann done:
1021*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
1022*1cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
1023*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ddev->struct_mutex);
102457e252bfSMichael Neumann }
102557e252bfSMichael Neumann 
10264cd92098Szrj void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
102757e252bfSMichael Neumann {
10284cd92098Szrj 	enum radeon_pm_state_type dpm_state;
102957e252bfSMichael Neumann 
10304cd92098Szrj 	if (rdev->asic->dpm.powergate_uvd) {
10314cd92098Szrj 		mutex_lock(&rdev->pm.mutex);
1032c6f73aabSFrançois Tigeot 		/* don't powergate anything if we
1033c6f73aabSFrançois Tigeot 		   have active but pause streams */
1034c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.sd > 0;
1035c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.hd > 0;
10364cd92098Szrj 		/* enable/disable UVD */
10374cd92098Szrj 		radeon_dpm_powergate_uvd(rdev, !enable);
10384cd92098Szrj 		mutex_unlock(&rdev->pm.mutex);
10394cd92098Szrj 	} else {
10404cd92098Szrj 		if (enable) {
10414cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
104257e252bfSMichael Neumann 			rdev->pm.dpm.uvd_active = true;
10434cd92098Szrj 			/* disable this for now */
10444cd92098Szrj #if 0
10454cd92098Szrj 			if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
10464cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
10474cd92098Szrj 			else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
10484cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10494cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1))
10504cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10514cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
10524cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
10534cd92098Szrj 			else
10544cd92098Szrj #endif
10554cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
105657e252bfSMichael Neumann 			rdev->pm.dpm.state = dpm_state;
10574cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
10584cd92098Szrj 		} else {
10594cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
10604cd92098Szrj 			rdev->pm.dpm.uvd_active = false;
10614cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
10624cd92098Szrj 		}
10634cd92098Szrj 
106457e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
106557e252bfSMichael Neumann 	}
10664cd92098Szrj }
106757e252bfSMichael Neumann 
1068c6f73aabSFrançois Tigeot void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable)
1069c6f73aabSFrançois Tigeot {
1070c6f73aabSFrançois Tigeot 	if (enable) {
1071c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1072c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = true;
1073c6f73aabSFrançois Tigeot 		/* XXX select vce level based on ring/task */
1074c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL;
1075c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1076c6f73aabSFrançois Tigeot 	} else {
1077c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1078c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = false;
1079c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1080c6f73aabSFrançois Tigeot 	}
1081c6f73aabSFrançois Tigeot 
1082c6f73aabSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1083c6f73aabSFrançois Tigeot }
1084c6f73aabSFrançois Tigeot 
108557e252bfSMichael Neumann static void radeon_pm_suspend_old(struct radeon_device *rdev)
1086926deccbSFrançois Tigeot {
1087*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1088926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1089926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE)
1090926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED;
1091926deccbSFrançois Tigeot 	}
1092*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1093926deccbSFrançois Tigeot 
1094ee479021SImre Vadász #ifdef DUMBBELL_WIP
1095926deccbSFrançois Tigeot 	cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1096ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1097926deccbSFrançois Tigeot }
1098926deccbSFrançois Tigeot 
109957e252bfSMichael Neumann static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
110057e252bfSMichael Neumann {
1101*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
110257e252bfSMichael Neumann 	/* disable dpm */
110357e252bfSMichael Neumann 	radeon_dpm_disable(rdev);
110457e252bfSMichael Neumann 	/* reset the power state */
110557e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
110657e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
1107*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
110857e252bfSMichael Neumann }
110957e252bfSMichael Neumann 
111057e252bfSMichael Neumann void radeon_pm_suspend(struct radeon_device *rdev)
111157e252bfSMichael Neumann {
111257e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
111357e252bfSMichael Neumann 		radeon_pm_suspend_dpm(rdev);
111457e252bfSMichael Neumann 	else
111557e252bfSMichael Neumann 		radeon_pm_suspend_old(rdev);
111657e252bfSMichael Neumann }
111757e252bfSMichael Neumann 
111857e252bfSMichael Neumann static void radeon_pm_resume_old(struct radeon_device *rdev)
1119926deccbSFrançois Tigeot {
1120926deccbSFrançois Tigeot 	/* set up the default clocks if the MC ucode is loaded */
1121926deccbSFrançois Tigeot 	if ((rdev->family >= CHIP_BARTS) &&
11224cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
1123926deccbSFrançois Tigeot 	    rdev->mc_fw) {
1124926deccbSFrançois Tigeot 		if (rdev->pm.default_vddc)
1125926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1126926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDC);
1127926deccbSFrançois Tigeot 		if (rdev->pm.default_vddci)
1128926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1129926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
1130926deccbSFrançois Tigeot 		if (rdev->pm.default_sclk)
1131926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1132926deccbSFrançois Tigeot 		if (rdev->pm.default_mclk)
1133926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1134926deccbSFrançois Tigeot 	}
1135926deccbSFrançois Tigeot 	/* asic init will reset the default power state */
1136*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1137926deccbSFrançois Tigeot 	rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
1138926deccbSFrançois Tigeot 	rdev->pm.current_clock_mode_index = 0;
1139926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->pm.default_sclk;
1140926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->pm.default_mclk;
1141c6f73aabSFrançois Tigeot 	if (rdev->pm.power_state) {
1142926deccbSFrançois Tigeot 		rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
1143926deccbSFrançois Tigeot 		rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
1144c6f73aabSFrançois Tigeot 	}
1145926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM
1146926deccbSFrançois Tigeot 	    && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
1147926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1148ee479021SImre Vadász #ifdef DUMBBELL_WIP
1149926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1150926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1151ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1152926deccbSFrançois Tigeot 	}
1153*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1154926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1155926deccbSFrançois Tigeot }
1156926deccbSFrançois Tigeot 
115757e252bfSMichael Neumann static void radeon_pm_resume_dpm(struct radeon_device *rdev)
1158926deccbSFrançois Tigeot {
1159926deccbSFrançois Tigeot 	int ret;
1160926deccbSFrançois Tigeot 
116157e252bfSMichael Neumann 	/* asic init will reset to the boot state */
1162*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
116357e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
116457e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
116557e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
1166*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1167c6f73aabSFrançois Tigeot 	if (ret)
1168c6f73aabSFrançois Tigeot 		goto dpm_resume_fail;
1169c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1170c6f73aabSFrançois Tigeot 	return;
1171c6f73aabSFrançois Tigeot 
1172c6f73aabSFrançois Tigeot dpm_resume_fail:
117357e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm resume failed\n");
117457e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
11754cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
117657e252bfSMichael Neumann 	    rdev->mc_fw) {
117757e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
117857e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
117957e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
118057e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
118157e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
118257e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
118357e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
118457e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
118557e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
118657e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
118757e252bfSMichael Neumann 	}
118857e252bfSMichael Neumann }
118957e252bfSMichael Neumann 
119057e252bfSMichael Neumann void radeon_pm_resume(struct radeon_device *rdev)
119157e252bfSMichael Neumann {
119257e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
119357e252bfSMichael Neumann 		radeon_pm_resume_dpm(rdev);
119457e252bfSMichael Neumann 	else
119557e252bfSMichael Neumann 		radeon_pm_resume_old(rdev);
119657e252bfSMichael Neumann }
119757e252bfSMichael Neumann 
119857e252bfSMichael Neumann static int radeon_pm_init_old(struct radeon_device *rdev)
119957e252bfSMichael Neumann {
120057e252bfSMichael Neumann 	int ret;
120157e252bfSMichael Neumann 
1202926deccbSFrançois Tigeot 	rdev->pm.profile = PM_PROFILE_DEFAULT;
1203926deccbSFrançois Tigeot 	rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1204926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1205926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_upclock = true;
1206926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_downclock = true;
1207926deccbSFrançois Tigeot 	rdev->pm.default_sclk = rdev->clock.default_sclk;
1208926deccbSFrançois Tigeot 	rdev->pm.default_mclk = rdev->clock.default_mclk;
1209926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->clock.default_sclk;
1210926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->clock.default_mclk;
1211926deccbSFrançois Tigeot 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
1212926deccbSFrançois Tigeot 
1213926deccbSFrançois Tigeot 	if (rdev->bios) {
1214926deccbSFrançois Tigeot 		if (rdev->is_atom_bios)
1215926deccbSFrançois Tigeot 			radeon_atombios_get_power_modes(rdev);
1216926deccbSFrançois Tigeot 		else
1217926deccbSFrançois Tigeot 			radeon_combios_get_power_modes(rdev);
1218926deccbSFrançois Tigeot 		radeon_pm_print_states(rdev);
1219926deccbSFrançois Tigeot 		radeon_pm_init_profile(rdev);
1220926deccbSFrançois Tigeot 		/* set up the default clocks if the MC ucode is loaded */
1221926deccbSFrançois Tigeot 		if ((rdev->family >= CHIP_BARTS) &&
12224cd92098Szrj 		    (rdev->family <= CHIP_CAYMAN) &&
1223926deccbSFrançois Tigeot 		    rdev->mc_fw) {
1224926deccbSFrançois Tigeot 			if (rdev->pm.default_vddc)
1225926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1226926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDC);
1227926deccbSFrançois Tigeot 			if (rdev->pm.default_vddci)
1228926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1229926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDCI);
1230926deccbSFrançois Tigeot 			if (rdev->pm.default_sclk)
1231926deccbSFrançois Tigeot 				radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1232926deccbSFrançois Tigeot 			if (rdev->pm.default_mclk)
1233926deccbSFrançois Tigeot 				radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1234926deccbSFrançois Tigeot 		}
1235926deccbSFrançois Tigeot 	}
1236926deccbSFrançois Tigeot 
1237926deccbSFrançois Tigeot 	/* set up the internal thermal sensor if applicable */
1238926deccbSFrançois Tigeot 	ret = radeon_hwmon_init(rdev);
1239926deccbSFrançois Tigeot 	if (ret)
1240926deccbSFrançois Tigeot 		return ret;
1241926deccbSFrançois Tigeot 
1242ee479021SImre Vadász #ifdef DUMBBELL_WIP
1243926deccbSFrançois Tigeot 	INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler);
1244ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1245926deccbSFrançois Tigeot 
1246926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1247ee479021SImre Vadász 		/* where's the best place to put these? */
1248ee479021SImre Vadász #ifdef DUMBBELL_WIP
1249ee479021SImre Vadász 		ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1250ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1251ee479021SImre Vadász 		if (ret)
1252ee479021SImre Vadász 			DRM_ERROR("failed to create device file for power profile\n");
1253ee479021SImre Vadász #ifdef DUMBBELL_WIP
1254ee479021SImre Vadász 		ret = device_create_file(rdev->dev, &dev_attr_power_method);
1255ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1256ee479021SImre Vadász 		if (ret)
1257ee479021SImre Vadász 			DRM_ERROR("failed to create device file for power method\n");
1258ee479021SImre Vadász 
1259926deccbSFrançois Tigeot 		if (radeon_debugfs_pm_init(rdev)) {
1260926deccbSFrançois Tigeot 			DRM_ERROR("Failed to register debugfs file for PM!\n");
1261926deccbSFrançois Tigeot 		}
1262926deccbSFrançois Tigeot 
1263926deccbSFrançois Tigeot 		DRM_INFO("radeon: power management initialized\n");
1264926deccbSFrançois Tigeot 	}
1265926deccbSFrançois Tigeot 
1266926deccbSFrançois Tigeot 	return 0;
1267926deccbSFrançois Tigeot }
1268926deccbSFrançois Tigeot 
126957e252bfSMichael Neumann static void radeon_dpm_print_power_states(struct radeon_device *rdev)
127057e252bfSMichael Neumann {
127157e252bfSMichael Neumann 	int i;
127257e252bfSMichael Neumann 
127357e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
127457e252bfSMichael Neumann 		printk("== power state %d ==\n", i);
127557e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
127657e252bfSMichael Neumann 	}
127757e252bfSMichael Neumann }
127857e252bfSMichael Neumann 
127957e252bfSMichael Neumann static int radeon_pm_init_dpm(struct radeon_device *rdev)
128057e252bfSMichael Neumann {
128157e252bfSMichael Neumann 	int ret;
128257e252bfSMichael Neumann 
12834cd92098Szrj 	/* default to balanced state */
128457e252bfSMichael Neumann 	rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
128557e252bfSMichael Neumann 	rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
12864cd92098Szrj 	rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
128757e252bfSMichael Neumann 	rdev->pm.default_sclk = rdev->clock.default_sclk;
128857e252bfSMichael Neumann 	rdev->pm.default_mclk = rdev->clock.default_mclk;
128957e252bfSMichael Neumann 	rdev->pm.current_sclk = rdev->clock.default_sclk;
129057e252bfSMichael Neumann 	rdev->pm.current_mclk = rdev->clock.default_mclk;
129157e252bfSMichael Neumann 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
129257e252bfSMichael Neumann 
129357e252bfSMichael Neumann 	if (rdev->bios && rdev->is_atom_bios)
129457e252bfSMichael Neumann 		radeon_atombios_get_power_modes(rdev);
129557e252bfSMichael Neumann 	else
129657e252bfSMichael Neumann 		return -EINVAL;
129757e252bfSMichael Neumann 
129857e252bfSMichael Neumann 	/* set up the internal thermal sensor if applicable */
129957e252bfSMichael Neumann 	ret = radeon_hwmon_init(rdev);
130057e252bfSMichael Neumann 	if (ret)
130157e252bfSMichael Neumann 		return ret;
130257e252bfSMichael Neumann 
13032c5cc6b9SFrançois Tigeot 	INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
13042c5cc6b9SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
130557e252bfSMichael Neumann 	radeon_dpm_init(rdev);
130657e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
1307c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1)
130857e252bfSMichael Neumann 		radeon_dpm_print_power_states(rdev);
130957e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
131057e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
13112c5cc6b9SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1312c6f73aabSFrançois Tigeot 	if (ret)
1313c6f73aabSFrançois Tigeot 		goto dpm_failed;
1314c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1315c6f73aabSFrançois Tigeot 
1316c6f73aabSFrançois Tigeot #ifdef TODO_DEVICE_FILE
1317ee479021SImre Vadász 	ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
1318ee479021SImre Vadász 	if (ret)
1319ee479021SImre Vadász 		DRM_ERROR("failed to create device file for dpm state\n");
1320ee479021SImre Vadász 	ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
1321ee479021SImre Vadász 	if (ret)
1322ee479021SImre Vadász 		DRM_ERROR("failed to create device file for dpm state\n");
1323ee479021SImre Vadász 	/* XXX: these are noops for dpm but are here for backwards compat */
1324ee479021SImre Vadász 	ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1325ee479021SImre Vadász 	if (ret)
1326ee479021SImre Vadász 		DRM_ERROR("failed to create device file for power profile\n");
1327ee479021SImre Vadász 	ret = device_create_file(rdev->dev, &dev_attr_power_method);
1328ee479021SImre Vadász 	if (ret)
1329ee479021SImre Vadász 		DRM_ERROR("failed to create device file for power method\n");
1330ee479021SImre Vadász 
1331c6f73aabSFrançois Tigeot 	if (radeon_debugfs_pm_init(rdev)) {
1332c6f73aabSFrançois Tigeot 		DRM_ERROR("Failed to register debugfs file for dpm!\n");
1333c6f73aabSFrançois Tigeot 	}
1334c6f73aabSFrançois Tigeot #endif
1335c6f73aabSFrançois Tigeot 
1336c6f73aabSFrançois Tigeot 	DRM_INFO("radeon: dpm initialized\n");
1337c6f73aabSFrançois Tigeot 
1338c6f73aabSFrançois Tigeot 	return 0;
1339c6f73aabSFrançois Tigeot 
1340c6f73aabSFrançois Tigeot dpm_failed:
134157e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
134257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
13434cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
134457e252bfSMichael Neumann 	    rdev->mc_fw) {
134557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
134657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
134757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
134857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
134957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
135057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
135157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
135257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
135357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
135457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
135557e252bfSMichael Neumann 	}
135657e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm initialization failed\n");
135757e252bfSMichael Neumann 	return ret;
135857e252bfSMichael Neumann }
135957e252bfSMichael Neumann 
136057e252bfSMichael Neumann int radeon_pm_init(struct radeon_device *rdev)
136157e252bfSMichael Neumann {
136257e252bfSMichael Neumann 	/* enable dpm on rv6xx+ */
136357e252bfSMichael Neumann 	switch (rdev->family) {
136457e252bfSMichael Neumann 	case CHIP_RV610:
136557e252bfSMichael Neumann 	case CHIP_RV630:
136657e252bfSMichael Neumann 	case CHIP_RV620:
136757e252bfSMichael Neumann 	case CHIP_RV635:
136857e252bfSMichael Neumann 	case CHIP_RV670:
136957e252bfSMichael Neumann 	case CHIP_RS780:
137057e252bfSMichael Neumann 	case CHIP_RS880:
137157e252bfSMichael Neumann 	case CHIP_RV770:
1372c6f73aabSFrançois Tigeot 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
1373c6f73aabSFrançois Tigeot 		if (!rdev->rlc_fw)
1374c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1375c6f73aabSFrançois Tigeot 		else if ((rdev->family >= CHIP_RV770) &&
1376c6f73aabSFrançois Tigeot 			 (!(rdev->flags & RADEON_IS_IGP)) &&
1377c6f73aabSFrançois Tigeot 			 (!rdev->smc_fw))
1378c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1379c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 1)
1380c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
1381c6f73aabSFrançois Tigeot 		else
1382c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1383c6f73aabSFrançois Tigeot 		break;
138457e252bfSMichael Neumann 	case CHIP_RV730:
138557e252bfSMichael Neumann 	case CHIP_RV710:
138657e252bfSMichael Neumann 	case CHIP_RV740:
138757e252bfSMichael Neumann 	case CHIP_CEDAR:
138857e252bfSMichael Neumann 	case CHIP_REDWOOD:
138957e252bfSMichael Neumann 	case CHIP_JUNIPER:
139057e252bfSMichael Neumann 	case CHIP_CYPRESS:
139157e252bfSMichael Neumann 	case CHIP_HEMLOCK:
139257e252bfSMichael Neumann 	case CHIP_PALM:
139357e252bfSMichael Neumann 	case CHIP_SUMO:
139457e252bfSMichael Neumann 	case CHIP_SUMO2:
139557e252bfSMichael Neumann 	case CHIP_BARTS:
139657e252bfSMichael Neumann 	case CHIP_TURKS:
139757e252bfSMichael Neumann 	case CHIP_CAICOS:
139857e252bfSMichael Neumann 	case CHIP_CAYMAN:
139957e252bfSMichael Neumann 	case CHIP_ARUBA:
140057e252bfSMichael Neumann 	case CHIP_TAHITI:
140157e252bfSMichael Neumann 	case CHIP_PITCAIRN:
140257e252bfSMichael Neumann 	case CHIP_VERDE:
140357e252bfSMichael Neumann 	case CHIP_OLAND:
140457e252bfSMichael Neumann 	case CHIP_HAINAN:
14054cd92098Szrj 	case CHIP_BONAIRE:
14064cd92098Szrj 	case CHIP_KABINI:
14074cd92098Szrj 	case CHIP_KAVERI:
1408c6f73aabSFrançois Tigeot 	case CHIP_HAWAII:
1409c6f73aabSFrançois Tigeot 	case CHIP_MULLINS:
141057e252bfSMichael Neumann 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
141157e252bfSMichael Neumann 		if (!rdev->rlc_fw)
141257e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
141357e252bfSMichael Neumann 		else if ((rdev->family >= CHIP_RV770) &&
141457e252bfSMichael Neumann 			 (!(rdev->flags & RADEON_IS_IGP)) &&
141557e252bfSMichael Neumann 			 (!rdev->smc_fw))
141657e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1417c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 0)
141857e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1419c6f73aabSFrançois Tigeot 		else
1420c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
142157e252bfSMichael Neumann 		break;
142257e252bfSMichael Neumann 	default:
142357e252bfSMichael Neumann 		/* default to profile method */
142457e252bfSMichael Neumann 		rdev->pm.pm_method = PM_METHOD_PROFILE;
142557e252bfSMichael Neumann 		break;
142657e252bfSMichael Neumann 	}
142757e252bfSMichael Neumann 
142857e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
142957e252bfSMichael Neumann 		return radeon_pm_init_dpm(rdev);
143057e252bfSMichael Neumann 	else
143157e252bfSMichael Neumann 		return radeon_pm_init_old(rdev);
143257e252bfSMichael Neumann }
143357e252bfSMichael Neumann 
1434c6f73aabSFrançois Tigeot int radeon_pm_late_init(struct radeon_device *rdev)
1435c6f73aabSFrançois Tigeot {
1436c6f73aabSFrançois Tigeot 	int ret = 0;
1437c6f73aabSFrançois Tigeot 
1438c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
1439*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1440c6f73aabSFrançois Tigeot 		ret = radeon_dpm_late_enable(rdev);
1441*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1442c6f73aabSFrançois Tigeot 	}
1443c6f73aabSFrançois Tigeot 	return ret;
1444c6f73aabSFrançois Tigeot }
1445c6f73aabSFrançois Tigeot 
144657e252bfSMichael Neumann static void radeon_pm_fini_old(struct radeon_device *rdev)
1447926deccbSFrançois Tigeot {
1448926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1449*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1450926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1451926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
1452926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
1453926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1454926deccbSFrançois Tigeot 		} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1455926deccbSFrançois Tigeot 			/* reset default clocks */
1456926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1457926deccbSFrançois Tigeot 			rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1458926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1459926deccbSFrançois Tigeot 		}
1460*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1461926deccbSFrançois Tigeot 
1462926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1463ee479021SImre Vadász 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1464ee479021SImre Vadász 
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) {
1486*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
148757e252bfSMichael Neumann 		radeon_dpm_disable(rdev);
1488*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
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 
15005d6a9071Szrj 	/* prevents leaking 440 bytes on OLAND */
15015d6a9071Szrj 	if (rdev->pm.power_state) {
15025d6a9071Szrj 		int i;
15035d6a9071Szrj 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
15045d6a9071Szrj 			kfree(rdev->pm.power_state[i].clock_info);
15055d6a9071Szrj 		}
150657e252bfSMichael Neumann 		kfree(rdev->pm.power_state);
15075d6a9071Szrj 		rdev->pm.power_state = NULL;
15085d6a9071Szrj 		rdev->pm.num_power_states = 0;
15095d6a9071Szrj 	}
151057e252bfSMichael Neumann 
151157e252bfSMichael Neumann 	radeon_hwmon_fini(rdev);
151257e252bfSMichael Neumann }
151357e252bfSMichael Neumann 
151457e252bfSMichael Neumann void radeon_pm_fini(struct radeon_device *rdev)
151557e252bfSMichael Neumann {
151657e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
151757e252bfSMichael Neumann 		radeon_pm_fini_dpm(rdev);
151857e252bfSMichael Neumann 	else
151957e252bfSMichael Neumann 		radeon_pm_fini_old(rdev);
152057e252bfSMichael Neumann }
152157e252bfSMichael Neumann 
152257e252bfSMichael Neumann static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
1523926deccbSFrançois Tigeot {
1524926deccbSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1525926deccbSFrançois Tigeot 	struct drm_crtc *crtc;
1526926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc;
1527926deccbSFrançois Tigeot 
1528926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states < 2)
1529926deccbSFrançois Tigeot 		return;
1530926deccbSFrançois Tigeot 
1531*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1532926deccbSFrançois Tigeot 
1533926deccbSFrançois Tigeot 	rdev->pm.active_crtcs = 0;
1534926deccbSFrançois Tigeot 	rdev->pm.active_crtc_count = 0;
1535c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
1536926deccbSFrançois Tigeot 		list_for_each_entry(crtc,
1537926deccbSFrançois Tigeot 				    &ddev->mode_config.crtc_list, head) {
1538926deccbSFrançois Tigeot 			radeon_crtc = to_radeon_crtc(crtc);
1539926deccbSFrançois Tigeot 			if (radeon_crtc->enabled) {
1540926deccbSFrançois Tigeot 				rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
1541926deccbSFrançois Tigeot 				rdev->pm.active_crtc_count++;
1542926deccbSFrançois Tigeot 			}
1543926deccbSFrançois Tigeot 		}
1544c6f73aabSFrançois Tigeot 	}
1545926deccbSFrançois Tigeot 
1546926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1547926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
1548926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
1549926deccbSFrançois Tigeot 	} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1550926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) {
1551926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1) {
1552926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1553ee479021SImre Vadász #ifdef DUMBBELL_WIP
1554926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1555ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1556926deccbSFrançois Tigeot 
1557926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
1558926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1559926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1560926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1561926deccbSFrançois Tigeot 
1562926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n");
1563926deccbSFrançois Tigeot 				}
1564926deccbSFrançois Tigeot 			} else if (rdev->pm.active_crtc_count == 1) {
1565926deccbSFrançois Tigeot 				/* TODO: Increase clocks if needed for current mode */
1566926deccbSFrançois Tigeot 
1567926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) {
1568926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1569926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK;
1570926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1571926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1572926deccbSFrançois Tigeot 
1573ee479021SImre Vadász #ifdef DUMBBELL_WIP
1574926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1575926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1576ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1577926deccbSFrançois Tigeot 				} else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) {
1578926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1579ee479021SImre Vadász #ifdef DUMBBELL_WIP
1580926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1581926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1582ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1583926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n");
1584926deccbSFrançois Tigeot 				}
1585926deccbSFrançois Tigeot 			} else { /* count == 0 */
1586926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) {
1587ee479021SImre Vadász #ifdef DUMBBELL_WIP
1588926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1589ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1590926deccbSFrançois Tigeot 
1591926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM;
1592926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM;
1593926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1594926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1595926deccbSFrançois Tigeot 				}
1596926deccbSFrançois Tigeot 			}
1597926deccbSFrançois Tigeot 		}
1598926deccbSFrançois Tigeot 	}
1599926deccbSFrançois Tigeot 
1600*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1601926deccbSFrançois Tigeot }
1602926deccbSFrançois Tigeot 
160357e252bfSMichael Neumann static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
160457e252bfSMichael Neumann {
160557e252bfSMichael Neumann 	struct drm_device *ddev = rdev->ddev;
160657e252bfSMichael Neumann 	struct drm_crtc *crtc;
160757e252bfSMichael Neumann 	struct radeon_crtc *radeon_crtc;
160857e252bfSMichael Neumann 
1609c6f73aabSFrançois Tigeot 	if (!rdev->pm.dpm_enabled)
1610c6f73aabSFrançois Tigeot 		return;
1611c6f73aabSFrançois Tigeot 
1612*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
161357e252bfSMichael Neumann 
161457e252bfSMichael Neumann 	/* update active crtc counts */
161557e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtcs = 0;
161657e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtc_count = 0;
1617c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
161857e252bfSMichael Neumann 		list_for_each_entry(crtc,
161957e252bfSMichael Neumann 				    &ddev->mode_config.crtc_list, head) {
162057e252bfSMichael Neumann 			radeon_crtc = to_radeon_crtc(crtc);
162157e252bfSMichael Neumann 			if (crtc->enabled) {
162257e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
162357e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtc_count++;
162457e252bfSMichael Neumann 			}
162557e252bfSMichael Neumann 		}
1626c6f73aabSFrançois Tigeot 	}
162757e252bfSMichael Neumann 
162857e252bfSMichael Neumann 	/* update battery/ac status */
162957e252bfSMichael Neumann 	if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
163057e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = true;
163157e252bfSMichael Neumann 	else
163257e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = false;
163357e252bfSMichael Neumann 
163457e252bfSMichael Neumann 	radeon_dpm_change_power_state_locked(rdev);
163557e252bfSMichael Neumann 
1636*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1637c6f73aabSFrançois Tigeot 
163857e252bfSMichael Neumann }
163957e252bfSMichael Neumann 
164057e252bfSMichael Neumann void radeon_pm_compute_clocks(struct radeon_device *rdev)
164157e252bfSMichael Neumann {
164257e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
164357e252bfSMichael Neumann 		radeon_pm_compute_clocks_dpm(rdev);
164457e252bfSMichael Neumann 	else
164557e252bfSMichael Neumann 		radeon_pm_compute_clocks_old(rdev);
164657e252bfSMichael Neumann }
164757e252bfSMichael Neumann 
1648926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev)
1649926deccbSFrançois Tigeot {
1650926deccbSFrançois Tigeot 	int  crtc, vpos, hpos, vbl_status;
1651926deccbSFrançois Tigeot 	bool in_vbl = true;
1652926deccbSFrançois Tigeot 
1653926deccbSFrançois Tigeot 	/* Iterate over all active crtc's. All crtc's must be in vblank,
1654926deccbSFrançois Tigeot 	 * otherwise return in_vbl == false.
1655926deccbSFrançois Tigeot 	 */
1656926deccbSFrançois Tigeot 	for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
1657926deccbSFrançois Tigeot 		if (rdev->pm.active_crtcs & (1 << crtc)) {
1658ee479021SImre Vadász 			vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0,
1659352ff8bdSFrançois Tigeot 								&vpos, &hpos, NULL, NULL,
1660352ff8bdSFrançois Tigeot 								&rdev->mode_info.crtcs[crtc]->base.hwmode);
1661926deccbSFrançois Tigeot 			if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
16621b13d190SFrançois Tigeot 			    !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK))
1663926deccbSFrançois Tigeot 				in_vbl = false;
1664926deccbSFrançois Tigeot 		}
1665926deccbSFrançois Tigeot 	}
1666926deccbSFrançois Tigeot 
1667926deccbSFrançois Tigeot 	return in_vbl;
1668926deccbSFrançois Tigeot }
1669926deccbSFrançois Tigeot 
1670926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
1671926deccbSFrançois Tigeot {
1672926deccbSFrançois Tigeot 	u32 stat_crtc = 0;
1673926deccbSFrançois Tigeot 	bool in_vbl = radeon_pm_in_vbl(rdev);
1674926deccbSFrançois Tigeot 
1675926deccbSFrançois Tigeot 	if (in_vbl == false)
1676926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc,
1677926deccbSFrançois Tigeot 			 finish ? "exit" : "entry");
1678926deccbSFrançois Tigeot 	return in_vbl;
1679926deccbSFrançois Tigeot }
1680926deccbSFrançois Tigeot 
1681ee479021SImre Vadász #ifdef DUMBBELL_WIP
1682926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work)
1683926deccbSFrançois Tigeot {
1684926deccbSFrançois Tigeot 	struct radeon_device *rdev;
1685926deccbSFrançois Tigeot 	int resched;
1686926deccbSFrançois Tigeot 	rdev = container_of(work, struct radeon_device,
1687926deccbSFrançois Tigeot 				pm.dynpm_idle_work.work);
1688926deccbSFrançois Tigeot 
1689926deccbSFrançois Tigeot 	resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
1690*1cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1691926deccbSFrançois Tigeot 	if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1692926deccbSFrançois Tigeot 		int not_processed = 0;
1693926deccbSFrançois Tigeot 		int i;
1694926deccbSFrançois Tigeot 
1695926deccbSFrançois Tigeot 		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
1696926deccbSFrançois Tigeot 			struct radeon_ring *ring = &rdev->ring[i];
1697926deccbSFrançois Tigeot 
1698926deccbSFrançois Tigeot 			if (ring->ready) {
1699926deccbSFrançois Tigeot 				not_processed += radeon_fence_count_emitted(rdev, i);
1700926deccbSFrançois Tigeot 				if (not_processed >= 3)
1701926deccbSFrançois Tigeot 					break;
1702926deccbSFrançois Tigeot 			}
1703926deccbSFrançois Tigeot 		}
1704926deccbSFrançois Tigeot 
1705926deccbSFrançois Tigeot 		if (not_processed >= 3) { /* should upclock */
1706926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) {
1707926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1708926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1709926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_upclock) {
1710926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1711926deccbSFrançois Tigeot 					DYNPM_ACTION_UPCLOCK;
1712926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1713926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1714926deccbSFrançois Tigeot 			}
1715926deccbSFrançois Tigeot 		} else if (not_processed == 0) { /* should downclock */
1716926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) {
1717926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1718926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1719926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_downclock) {
1720926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1721926deccbSFrançois Tigeot 					DYNPM_ACTION_DOWNCLOCK;
1722926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1723926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1724926deccbSFrançois Tigeot 			}
1725926deccbSFrançois Tigeot 		}
1726926deccbSFrançois Tigeot 
1727926deccbSFrançois Tigeot 		/* Note, radeon_pm_set_clocks is called with static_switch set
1728926deccbSFrançois Tigeot 		 * to false since we want to wait for vbl to avoid flicker.
1729926deccbSFrançois Tigeot 		 */
1730926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE &&
1731926deccbSFrançois Tigeot 		    jiffies > rdev->pm.dynpm_action_timeout) {
1732926deccbSFrançois Tigeot 			radeon_pm_get_dynpm_state(rdev);
1733926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1734926deccbSFrançois Tigeot 		}
1735926deccbSFrançois Tigeot 
1736926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1737926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1738926deccbSFrançois Tigeot 	}
1739*1cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1740926deccbSFrançois Tigeot 	ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
1741926deccbSFrançois Tigeot }
1742ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1743926deccbSFrançois Tigeot 
1744926deccbSFrançois Tigeot /*
1745926deccbSFrançois Tigeot  * Debugfs info
1746926deccbSFrançois Tigeot  */
1747926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1748926deccbSFrançois Tigeot 
1749926deccbSFrançois Tigeot static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
1750926deccbSFrançois Tigeot {
1751926deccbSFrançois Tigeot 	struct drm_info_node *node = (struct drm_info_node *) m->private;
1752926deccbSFrançois Tigeot 	struct drm_device *dev = node->minor->dev;
1753926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
1754c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1755926deccbSFrançois Tigeot 
1756c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
1757c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
1758c6f73aabSFrançois Tigeot 		seq_printf(m, "PX asic powered off\n");
1759c6f73aabSFrançois Tigeot 	} else if (rdev->pm.dpm_enabled) {
1760*1cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
176157e252bfSMichael Neumann 		if (rdev->asic->dpm.debugfs_print_current_performance_level)
176257e252bfSMichael Neumann 			radeon_dpm_debugfs_print_current_performance_level(rdev, m);
176357e252bfSMichael Neumann 		else
176457e252bfSMichael Neumann 			seq_printf(m, "Debugfs support not implemented for this asic\n");
1765*1cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
176657e252bfSMichael Neumann 	} else {
1767926deccbSFrançois Tigeot 		seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
1768f43cf1b1SMichael Neumann 		/* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
1769f43cf1b1SMichael Neumann 		if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
1770f43cf1b1SMichael Neumann 			seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
1771f43cf1b1SMichael Neumann 		else
1772926deccbSFrançois Tigeot 			seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
1773926deccbSFrançois Tigeot 		seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
1774926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_memory_clock)
1775926deccbSFrançois Tigeot 			seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
1776926deccbSFrançois Tigeot 		if (rdev->pm.current_vddc)
1777926deccbSFrançois Tigeot 			seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
1778926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_pcie_lanes)
1779926deccbSFrançois Tigeot 			seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
178057e252bfSMichael Neumann 	}
1781926deccbSFrançois Tigeot 
1782926deccbSFrançois Tigeot 	return 0;
1783926deccbSFrançois Tigeot }
1784926deccbSFrançois Tigeot 
1785926deccbSFrançois Tigeot static struct drm_info_list radeon_pm_info_list[] = {
1786926deccbSFrançois Tigeot 	{"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL},
1787926deccbSFrançois Tigeot };
1788926deccbSFrançois Tigeot #endif
1789926deccbSFrançois Tigeot 
1790926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev)
1791926deccbSFrançois Tigeot {
1792926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1793926deccbSFrançois Tigeot 	return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
1794926deccbSFrançois Tigeot #else
1795926deccbSFrançois Tigeot 	return 0;
1796926deccbSFrançois Tigeot #endif
1797926deccbSFrançois Tigeot }
1798