xref: /dflybsd-src/sys/dev/drm/radeon/radeon_pm.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
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"
27c59a5c48SFrançois Tigeot #include "r600_dpm.h"
281cfef1a5SFrançois Tigeot #include <linux/power_supply.h>
291cfef1a5SFrançois Tigeot #include <linux/hwmon.h>
301cfef1a5SFrançois Tigeot 
311cfef1a5SFrançois Tigeot #include <sys/power.h>
321cfef1a5SFrançois Tigeot #include <sys/sensors.h>
33926deccbSFrançois Tigeot 
34926deccbSFrançois Tigeot #define RADEON_IDLE_LOOP_MS 100
35926deccbSFrançois Tigeot #define RADEON_RECLOCK_DELAY_MS 200
36926deccbSFrançois Tigeot #define RADEON_WAIT_VBLANK_TIMEOUT 200
37926deccbSFrançois Tigeot 
38926deccbSFrançois Tigeot static const char *radeon_pm_state_type_name[5] = {
39926deccbSFrançois Tigeot 	"",
40926deccbSFrançois Tigeot 	"Powersave",
41926deccbSFrançois Tigeot 	"Battery",
42926deccbSFrançois Tigeot 	"Balanced",
43926deccbSFrançois Tigeot 	"Performance",
44926deccbSFrançois Tigeot };
45926deccbSFrançois Tigeot 
46926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work);
47926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev);
48926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev);
49926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
50926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev);
51926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev);
52926deccbSFrançois Tigeot 
radeon_pm_get_type_index(struct radeon_device * rdev,enum radeon_pm_state_type ps_type,int instance)53926deccbSFrançois Tigeot int radeon_pm_get_type_index(struct radeon_device *rdev,
54926deccbSFrançois Tigeot 			     enum radeon_pm_state_type ps_type,
55926deccbSFrançois Tigeot 			     int instance)
56926deccbSFrançois Tigeot {
57926deccbSFrançois Tigeot 	int i;
58926deccbSFrançois Tigeot 	int found_instance = -1;
59926deccbSFrançois Tigeot 
60926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
61926deccbSFrançois Tigeot 		if (rdev->pm.power_state[i].type == ps_type) {
62926deccbSFrançois Tigeot 			found_instance++;
63926deccbSFrançois Tigeot 			if (found_instance == instance)
64926deccbSFrançois Tigeot 				return i;
65926deccbSFrançois Tigeot 		}
66926deccbSFrançois Tigeot 	}
67926deccbSFrançois Tigeot 	/* return default if no match */
68926deccbSFrançois Tigeot 	return rdev->pm.default_power_state_index;
69926deccbSFrançois Tigeot }
70926deccbSFrançois Tigeot 
radeon_pm_acpi_event_handler(struct radeon_device * rdev)71926deccbSFrançois Tigeot void radeon_pm_acpi_event_handler(struct radeon_device *rdev)
72926deccbSFrançois Tigeot {
734cd92098Szrj 	if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
741cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
75c6f73aabSFrançois Tigeot 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
764cd92098Szrj 			rdev->pm.dpm.ac_power = true;
774cd92098Szrj 		else
784cd92098Szrj 			rdev->pm.dpm.ac_power = false;
79c6f73aabSFrançois Tigeot 		if (rdev->family == CHIP_ARUBA) {
804cd92098Szrj 			if (rdev->asic->dpm.enable_bapm)
814cd92098Szrj 				radeon_dpm_enable_bapm(rdev, rdev->pm.dpm.ac_power);
82c6f73aabSFrançois Tigeot 		}
831cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
844cd92098Szrj 	} else if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
85926deccbSFrançois Tigeot 		if (rdev->pm.profile == PM_PROFILE_AUTO) {
861cfef1a5SFrançois Tigeot 			mutex_lock(&rdev->pm.mutex);
87926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
88926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
891cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->pm.mutex);
90926deccbSFrançois Tigeot 		}
91926deccbSFrançois Tigeot 	}
92926deccbSFrançois Tigeot }
93926deccbSFrançois Tigeot 
radeon_pm_update_profile(struct radeon_device * rdev)94926deccbSFrançois Tigeot static void radeon_pm_update_profile(struct radeon_device *rdev)
95926deccbSFrançois Tigeot {
96926deccbSFrançois Tigeot 	switch (rdev->pm.profile) {
97926deccbSFrançois Tigeot 	case PM_PROFILE_DEFAULT:
98926deccbSFrançois Tigeot 		rdev->pm.profile_index = PM_PROFILE_DEFAULT_IDX;
99926deccbSFrançois Tigeot 		break;
100926deccbSFrançois Tigeot 	case PM_PROFILE_AUTO:
10157e252bfSMichael Neumann 		if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) {
102926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
103926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
104926deccbSFrançois Tigeot 			else
105926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
106926deccbSFrançois Tigeot 		} else {
107926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1)
108926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
109926deccbSFrançois Tigeot 			else
110926deccbSFrançois Tigeot 				rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
111926deccbSFrançois Tigeot 		}
112926deccbSFrançois Tigeot 		break;
113926deccbSFrançois Tigeot 	case PM_PROFILE_LOW:
114926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
115926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_MH_IDX;
116926deccbSFrançois Tigeot 		else
117926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_LOW_SH_IDX;
118926deccbSFrançois Tigeot 		break;
119926deccbSFrançois Tigeot 	case PM_PROFILE_MID:
120926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
121926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_MH_IDX;
122926deccbSFrançois Tigeot 		else
123926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_MID_SH_IDX;
124926deccbSFrançois Tigeot 		break;
125926deccbSFrançois Tigeot 	case PM_PROFILE_HIGH:
126926deccbSFrançois Tigeot 		if (rdev->pm.active_crtc_count > 1)
127926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_MH_IDX;
128926deccbSFrançois Tigeot 		else
129926deccbSFrançois Tigeot 			rdev->pm.profile_index = PM_PROFILE_HIGH_SH_IDX;
130926deccbSFrançois Tigeot 		break;
131926deccbSFrançois Tigeot 	}
132926deccbSFrançois Tigeot 
133926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count == 0) {
134926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
135926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_ps_idx;
136926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
137926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_off_cm_idx;
138926deccbSFrançois Tigeot 	} else {
139926deccbSFrançois Tigeot 		rdev->pm.requested_power_state_index =
140926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx;
141926deccbSFrançois Tigeot 		rdev->pm.requested_clock_mode_index =
142926deccbSFrançois Tigeot 			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx;
143926deccbSFrançois Tigeot 	}
144926deccbSFrançois Tigeot }
145926deccbSFrançois Tigeot 
radeon_unmap_vram_bos(struct radeon_device * rdev)146926deccbSFrançois Tigeot static void radeon_unmap_vram_bos(struct radeon_device *rdev)
147926deccbSFrançois Tigeot {
148926deccbSFrançois Tigeot 	struct radeon_bo *bo, *n;
149926deccbSFrançois Tigeot 
150926deccbSFrançois Tigeot 	if (list_empty(&rdev->gem.objects))
151926deccbSFrançois Tigeot 		return;
152926deccbSFrançois Tigeot 
153926deccbSFrançois Tigeot 	list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
154926deccbSFrançois Tigeot 		if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
155926deccbSFrançois Tigeot 			ttm_bo_unmap_virtual(&bo->tbo);
156926deccbSFrançois Tigeot 	}
157926deccbSFrançois Tigeot }
158926deccbSFrançois Tigeot 
radeon_sync_with_vblank(struct radeon_device * rdev)159926deccbSFrançois Tigeot static void radeon_sync_with_vblank(struct radeon_device *rdev)
160926deccbSFrançois Tigeot {
161926deccbSFrançois Tigeot 	if (rdev->pm.active_crtcs) {
162926deccbSFrançois Tigeot 		rdev->pm.vblank_sync = false;
163ee479021SImre Vadász #ifdef DUMBBELL_WIP
164926deccbSFrançois Tigeot 		wait_event_timeout(
165926deccbSFrançois Tigeot 			rdev->irq.vblank_queue, rdev->pm.vblank_sync,
166926deccbSFrançois Tigeot 			msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
167ee479021SImre Vadász #endif /* DUMBBELL_WIP */
168926deccbSFrançois Tigeot 	}
169926deccbSFrançois Tigeot }
170926deccbSFrançois Tigeot 
radeon_set_power_state(struct radeon_device * rdev)171926deccbSFrançois Tigeot static void radeon_set_power_state(struct radeon_device *rdev)
172926deccbSFrançois Tigeot {
173926deccbSFrançois Tigeot 	u32 sclk, mclk;
174926deccbSFrançois Tigeot 	bool misc_after = false;
175926deccbSFrançois Tigeot 
176926deccbSFrançois Tigeot 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
177926deccbSFrançois Tigeot 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
178926deccbSFrançois Tigeot 		return;
179926deccbSFrançois Tigeot 
180926deccbSFrançois Tigeot 	if (radeon_gui_idle(rdev)) {
181926deccbSFrançois Tigeot 		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
182926deccbSFrançois Tigeot 			clock_info[rdev->pm.requested_clock_mode_index].sclk;
183926deccbSFrançois Tigeot 		if (sclk > rdev->pm.default_sclk)
184926deccbSFrançois Tigeot 			sclk = rdev->pm.default_sclk;
185926deccbSFrançois Tigeot 
186926deccbSFrançois Tigeot 		/* starting with BTC, there is one state that is used for both
187926deccbSFrançois Tigeot 		 * MH and SH.  Difference is that we always use the high clock index for
188b403bed8SMichael Neumann 		 * mclk and vddci.
189926deccbSFrançois Tigeot 		 */
190926deccbSFrançois Tigeot 		if ((rdev->pm.pm_method == PM_METHOD_PROFILE) &&
191926deccbSFrançois Tigeot 		    (rdev->family >= CHIP_BARTS) &&
192926deccbSFrançois Tigeot 		    rdev->pm.active_crtc_count &&
193926deccbSFrançois Tigeot 		    ((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) ||
194926deccbSFrançois Tigeot 		     (rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX)))
195926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
196926deccbSFrançois Tigeot 				clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].mclk;
197926deccbSFrançois Tigeot 		else
198926deccbSFrançois Tigeot 			mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
199926deccbSFrançois Tigeot 				clock_info[rdev->pm.requested_clock_mode_index].mclk;
200926deccbSFrançois Tigeot 
201926deccbSFrançois Tigeot 		if (mclk > rdev->pm.default_mclk)
202926deccbSFrançois Tigeot 			mclk = rdev->pm.default_mclk;
203926deccbSFrançois Tigeot 
204926deccbSFrançois Tigeot 		/* upvolt before raising clocks, downvolt after lowering clocks */
205926deccbSFrançois Tigeot 		if (sclk < rdev->pm.current_sclk)
206926deccbSFrançois Tigeot 			misc_after = true;
207926deccbSFrançois Tigeot 
208926deccbSFrançois Tigeot 		radeon_sync_with_vblank(rdev);
209926deccbSFrançois Tigeot 
210926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
211926deccbSFrançois Tigeot 			if (!radeon_pm_in_vbl(rdev))
212926deccbSFrançois Tigeot 				return;
213926deccbSFrançois Tigeot 		}
214926deccbSFrançois Tigeot 
215926deccbSFrançois Tigeot 		radeon_pm_prepare(rdev);
216926deccbSFrançois Tigeot 
217926deccbSFrançois Tigeot 		if (!misc_after)
218926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
219926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
220926deccbSFrançois Tigeot 
221926deccbSFrançois Tigeot 		/* set engine clock */
222926deccbSFrançois Tigeot 		if (sclk != rdev->pm.current_sclk) {
223926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
224926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, sclk);
225926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
226926deccbSFrançois Tigeot 			rdev->pm.current_sclk = sclk;
227926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: e: %d\n", sclk);
228926deccbSFrançois Tigeot 		}
229926deccbSFrançois Tigeot 
230926deccbSFrançois Tigeot 		/* set memory clock */
231926deccbSFrançois Tigeot 		if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) {
232926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, false);
233926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, mclk);
234926deccbSFrançois Tigeot 			radeon_pm_debug_check_in_vbl(rdev, true);
235926deccbSFrançois Tigeot 			rdev->pm.current_mclk = mclk;
236926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("Setting: m: %d\n", mclk);
237926deccbSFrançois Tigeot 		}
238926deccbSFrançois Tigeot 
239926deccbSFrançois Tigeot 		if (misc_after)
240926deccbSFrançois Tigeot 			/* voltage, pcie lanes, etc.*/
241926deccbSFrançois Tigeot 			radeon_pm_misc(rdev);
242926deccbSFrançois Tigeot 
243926deccbSFrançois Tigeot 		radeon_pm_finish(rdev);
244926deccbSFrançois Tigeot 
245926deccbSFrançois Tigeot 		rdev->pm.current_power_state_index = rdev->pm.requested_power_state_index;
246926deccbSFrançois Tigeot 		rdev->pm.current_clock_mode_index = rdev->pm.requested_clock_mode_index;
247926deccbSFrançois Tigeot 	} else
248926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("pm: GUI not idle!!!\n");
249926deccbSFrançois Tigeot }
250926deccbSFrançois Tigeot 
radeon_pm_set_clocks(struct radeon_device * rdev)251926deccbSFrançois Tigeot static void radeon_pm_set_clocks(struct radeon_device *rdev)
252926deccbSFrançois Tigeot {
2531dedbd3bSFrançois Tigeot 	struct drm_crtc *crtc;
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 
2611cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
2621cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
263926deccbSFrançois Tigeot 
264926deccbSFrançois Tigeot 	/* wait for the rings to drain */
265926deccbSFrançois Tigeot 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
266926deccbSFrançois Tigeot 		struct radeon_ring *ring = &rdev->ring[i];
267926deccbSFrançois Tigeot 		if (!ring->ready) {
268926deccbSFrançois Tigeot 			continue;
269926deccbSFrançois Tigeot 		}
270c6f73aabSFrançois Tigeot 		r = radeon_fence_wait_empty(rdev, i);
271926deccbSFrançois Tigeot 		if (r) {
272926deccbSFrançois Tigeot 			/* needs a GPU reset dont reset here */
2731cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->ring_lock);
2741cfef1a5SFrançois Tigeot 			up_write(&rdev->pm.mclk_lock);
275926deccbSFrançois Tigeot 			return;
276926deccbSFrançois Tigeot 		}
277926deccbSFrançois Tigeot 	}
278926deccbSFrançois Tigeot 
279926deccbSFrançois Tigeot 	radeon_unmap_vram_bos(rdev);
280926deccbSFrançois Tigeot 
281926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
2821dedbd3bSFrançois Tigeot 		i = 0;
2831dedbd3bSFrançois Tigeot 		drm_for_each_crtc(crtc, rdev->ddev) {
284926deccbSFrançois Tigeot 			if (rdev->pm.active_crtcs & (1 << i)) {
285d78d3a22SFrançois Tigeot 				/* This can fail if a modeset is in progress */
2861dedbd3bSFrançois Tigeot 				if (drm_crtc_vblank_get(crtc) == 0)
287926deccbSFrançois Tigeot 					rdev->pm.req_vblank |= (1 << i);
288d78d3a22SFrançois Tigeot 				else
289d78d3a22SFrançois Tigeot 					DRM_DEBUG_DRIVER("crtc %d no vblank, can glitch\n",
290d78d3a22SFrançois Tigeot 							 i);
291926deccbSFrançois Tigeot 			}
2921dedbd3bSFrançois Tigeot 			i++;
293926deccbSFrançois Tigeot 		}
294926deccbSFrançois Tigeot 	}
295926deccbSFrançois Tigeot 
296926deccbSFrançois Tigeot 	radeon_set_power_state(rdev);
297926deccbSFrançois Tigeot 
298926deccbSFrançois Tigeot 	if (rdev->irq.installed) {
2991dedbd3bSFrançois Tigeot 		i = 0;
3001dedbd3bSFrançois Tigeot 		drm_for_each_crtc(crtc, rdev->ddev) {
301926deccbSFrançois Tigeot 			if (rdev->pm.req_vblank & (1 << i)) {
302926deccbSFrançois Tigeot 				rdev->pm.req_vblank &= ~(1 << i);
3031dedbd3bSFrançois Tigeot 				drm_crtc_vblank_put(crtc);
304926deccbSFrançois Tigeot 			}
3051dedbd3bSFrançois Tigeot 			i++;
306926deccbSFrançois Tigeot 		}
307926deccbSFrançois Tigeot 	}
308926deccbSFrançois Tigeot 
309926deccbSFrançois Tigeot 	/* update display watermarks based on new power state */
310926deccbSFrançois Tigeot 	radeon_update_bandwidth_info(rdev);
311926deccbSFrançois Tigeot 	if (rdev->pm.active_crtc_count)
312926deccbSFrançois Tigeot 		radeon_bandwidth_update(rdev);
313926deccbSFrançois Tigeot 
314926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
315926deccbSFrançois Tigeot 
3161cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
3171cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
318926deccbSFrançois Tigeot }
319926deccbSFrançois Tigeot 
radeon_pm_print_states(struct radeon_device * rdev)320926deccbSFrançois Tigeot static void radeon_pm_print_states(struct radeon_device *rdev)
321926deccbSFrançois Tigeot {
322926deccbSFrançois Tigeot 	int i, j;
323926deccbSFrançois Tigeot 	struct radeon_power_state *power_state;
324926deccbSFrançois Tigeot 	struct radeon_pm_clock_info *clock_info;
325926deccbSFrançois Tigeot 
326926deccbSFrançois Tigeot 	DRM_DEBUG_DRIVER("%d Power State(s)\n", rdev->pm.num_power_states);
327926deccbSFrançois Tigeot 	for (i = 0; i < rdev->pm.num_power_states; i++) {
328926deccbSFrançois Tigeot 		power_state = &rdev->pm.power_state[i];
329926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("State %d: %s\n", i,
330926deccbSFrançois Tigeot 			radeon_pm_state_type_name[power_state->type]);
331926deccbSFrançois Tigeot 		if (i == rdev->pm.default_power_state_index)
332926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tDefault");
333926deccbSFrançois Tigeot 		if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
334926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\t%d PCIE Lanes\n", power_state->pcie_lanes);
335926deccbSFrançois Tigeot 		if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY)
336926deccbSFrançois Tigeot 			DRM_DEBUG_DRIVER("\tSingle display only\n");
337926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("\t%d Clock Mode(s)\n", power_state->num_clock_modes);
338926deccbSFrançois Tigeot 		for (j = 0; j < power_state->num_clock_modes; j++) {
339926deccbSFrançois Tigeot 			clock_info = &(power_state->clock_info[j]);
340926deccbSFrançois Tigeot 			if (rdev->flags & RADEON_IS_IGP)
341926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\n",
342926deccbSFrançois Tigeot 						 j,
343926deccbSFrançois Tigeot 						 clock_info->sclk * 10);
344926deccbSFrançois Tigeot 			else
345926deccbSFrançois Tigeot 				DRM_DEBUG_DRIVER("\t\t%d e: %d\tm: %d\tv: %d\n",
346926deccbSFrançois Tigeot 						 j,
347926deccbSFrançois Tigeot 						 clock_info->sclk * 10,
348926deccbSFrançois Tigeot 						 clock_info->mclk * 10,
349926deccbSFrançois Tigeot 						 clock_info->voltage.voltage);
350926deccbSFrançois Tigeot 		}
351926deccbSFrançois Tigeot 	}
352926deccbSFrançois Tigeot }
353926deccbSFrançois Tigeot 
354926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
radeon_get_pm_profile(struct device * dev,struct device_attribute * attr,char * buf)355926deccbSFrançois Tigeot static ssize_t radeon_get_pm_profile(struct device *dev,
356926deccbSFrançois Tigeot 				     struct device_attribute *attr,
357926deccbSFrançois Tigeot 				     char *buf)
358926deccbSFrançois Tigeot {
359c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
360926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
361926deccbSFrançois Tigeot 	int cp = rdev->pm.profile;
362926deccbSFrançois Tigeot 
363926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
364926deccbSFrançois Tigeot 			(cp == PM_PROFILE_AUTO) ? "auto" :
365926deccbSFrançois Tigeot 			(cp == PM_PROFILE_LOW) ? "low" :
366926deccbSFrançois Tigeot 			(cp == PM_PROFILE_MID) ? "mid" :
367926deccbSFrançois Tigeot 			(cp == PM_PROFILE_HIGH) ? "high" : "default");
368926deccbSFrançois Tigeot }
369926deccbSFrançois Tigeot 
radeon_set_pm_profile(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)370926deccbSFrançois Tigeot static ssize_t radeon_set_pm_profile(struct device *dev,
371926deccbSFrançois Tigeot 				     struct device_attribute *attr,
372926deccbSFrançois Tigeot 				     const char *buf,
373926deccbSFrançois Tigeot 				     size_t count)
374926deccbSFrançois Tigeot {
375c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
376926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
377926deccbSFrançois Tigeot 
378c6f73aabSFrançois Tigeot 	/* Can't set profile when the card is off */
379c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
380c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
381c6f73aabSFrançois Tigeot 		return -EINVAL;
382c6f73aabSFrançois Tigeot 
3831cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
384926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
385926deccbSFrançois Tigeot 		if (strncmp("default", buf, strlen("default")) == 0)
386926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
387926deccbSFrançois Tigeot 		else if (strncmp("auto", buf, strlen("auto")) == 0)
388926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_AUTO;
389926deccbSFrançois Tigeot 		else if (strncmp("low", buf, strlen("low")) == 0)
390926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_LOW;
391926deccbSFrançois Tigeot 		else if (strncmp("mid", buf, strlen("mid")) == 0)
392926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_MID;
393926deccbSFrançois Tigeot 		else if (strncmp("high", buf, strlen("high")) == 0)
394926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_HIGH;
395926deccbSFrançois Tigeot 		else {
396926deccbSFrançois Tigeot 			count = -EINVAL;
397926deccbSFrançois Tigeot 			goto fail;
398926deccbSFrançois Tigeot 		}
399926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
400926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
401926deccbSFrançois Tigeot 	} else
402926deccbSFrançois Tigeot 		count = -EINVAL;
403926deccbSFrançois Tigeot 
404926deccbSFrançois Tigeot fail:
4051cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
406926deccbSFrançois Tigeot 
407926deccbSFrançois Tigeot 	return count;
408926deccbSFrançois Tigeot }
409926deccbSFrançois Tigeot 
radeon_get_pm_method(struct device * dev,struct device_attribute * attr,char * buf)410926deccbSFrançois Tigeot static ssize_t radeon_get_pm_method(struct device *dev,
411926deccbSFrançois Tigeot 				    struct device_attribute *attr,
412926deccbSFrançois Tigeot 				    char *buf)
413926deccbSFrançois Tigeot {
414c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
415926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
416926deccbSFrançois Tigeot 	int pm = rdev->pm.pm_method;
417926deccbSFrançois Tigeot 
418926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%s\n",
41957e252bfSMichael Neumann 			(pm == PM_METHOD_DYNPM) ? "dynpm" :
42057e252bfSMichael Neumann 			(pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
421926deccbSFrançois Tigeot }
422926deccbSFrançois Tigeot 
radeon_set_pm_method(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)423926deccbSFrançois Tigeot static ssize_t radeon_set_pm_method(struct device *dev,
424926deccbSFrançois Tigeot 				    struct device_attribute *attr,
425926deccbSFrançois Tigeot 				    const char *buf,
426926deccbSFrançois Tigeot 				    size_t count)
427926deccbSFrançois Tigeot {
428c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
429926deccbSFrançois Tigeot 	struct radeon_device *rdev = ddev->dev_private;
430926deccbSFrançois Tigeot 
431c6f73aabSFrançois Tigeot 	/* Can't set method when the card is off */
432c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
433c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
434c6f73aabSFrançois Tigeot 		count = -EINVAL;
435c6f73aabSFrançois Tigeot 		goto fail;
436c6f73aabSFrançois Tigeot 	}
437c6f73aabSFrançois Tigeot 
43857e252bfSMichael Neumann 	/* we don't support the legacy modes with dpm */
43957e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
44057e252bfSMichael Neumann 		count = -EINVAL;
44157e252bfSMichael Neumann 		goto fail;
44257e252bfSMichael Neumann 	}
443926deccbSFrançois Tigeot 
444926deccbSFrançois Tigeot 	if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
4451cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
446926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_DYNPM;
447926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
448926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
4491cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
450926deccbSFrançois Tigeot 	} else if (strncmp("profile", buf, strlen("profile")) == 0) {
4511cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
452926deccbSFrançois Tigeot 		/* disable dynpm */
453926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
454926deccbSFrançois Tigeot 		rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
455926deccbSFrançois Tigeot 		rdev->pm.pm_method = PM_METHOD_PROFILE;
4561cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
457ee479021SImre Vadász #ifdef DUMBBELL_WIP
458926deccbSFrançois Tigeot 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
459ee479021SImre Vadász #endif /* DUMBBELL_WIP */
460926deccbSFrançois Tigeot 	} else {
461926deccbSFrançois Tigeot 		count = -EINVAL;
462926deccbSFrançois Tigeot 		goto fail;
463926deccbSFrançois Tigeot 	}
464926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
465926deccbSFrançois Tigeot fail:
466926deccbSFrançois Tigeot 	return count;
467926deccbSFrançois Tigeot }
468926deccbSFrançois Tigeot 
radeon_get_dpm_state(struct device * dev,struct device_attribute * attr,char * buf)46957e252bfSMichael Neumann static ssize_t radeon_get_dpm_state(struct device *dev,
47057e252bfSMichael Neumann 				    struct device_attribute *attr,
47157e252bfSMichael Neumann 				    char *buf)
47257e252bfSMichael Neumann {
473c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
47457e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
47557e252bfSMichael Neumann 	enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
47657e252bfSMichael Neumann 
47757e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
47857e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
47957e252bfSMichael Neumann 			(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
48057e252bfSMichael Neumann }
48157e252bfSMichael Neumann 
radeon_set_dpm_state(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)48257e252bfSMichael Neumann static ssize_t radeon_set_dpm_state(struct device *dev,
48357e252bfSMichael Neumann 				    struct device_attribute *attr,
48457e252bfSMichael Neumann 				    const char *buf,
48557e252bfSMichael Neumann 				    size_t count)
48657e252bfSMichael Neumann {
487c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
48857e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
48957e252bfSMichael Neumann 
4901cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
49157e252bfSMichael Neumann 	if (strncmp("battery", buf, strlen("battery")) == 0)
49257e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
49357e252bfSMichael Neumann 	else if (strncmp("balanced", buf, strlen("balanced")) == 0)
49457e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
49557e252bfSMichael Neumann 	else if (strncmp("performance", buf, strlen("performance")) == 0)
49657e252bfSMichael Neumann 		rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
49757e252bfSMichael Neumann 	else {
4981cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
49957e252bfSMichael Neumann 		count = -EINVAL;
50057e252bfSMichael Neumann 		goto fail;
50157e252bfSMichael Neumann 	}
5021cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
503c6f73aabSFrançois Tigeot 
504c6f73aabSFrançois Tigeot 	/* Can't set dpm state when the card is off */
505c6f73aabSFrançois Tigeot 	if (!(rdev->flags & RADEON_IS_PX) ||
506c6f73aabSFrançois Tigeot 	    (ddev->switch_power_state == DRM_SWITCH_POWER_ON))
50757e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
508c6f73aabSFrançois Tigeot 
50957e252bfSMichael Neumann fail:
51057e252bfSMichael Neumann 	return count;
51157e252bfSMichael Neumann }
51257e252bfSMichael Neumann 
radeon_get_dpm_forced_performance_level(struct device * dev,struct device_attribute * attr,char * buf)51357e252bfSMichael Neumann static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
51457e252bfSMichael Neumann 						       struct device_attribute *attr,
51557e252bfSMichael Neumann 						       char *buf)
51657e252bfSMichael Neumann {
517c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
51857e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
51957e252bfSMichael Neumann 	enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
52057e252bfSMichael Neumann 
521c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
522c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
5231cfef1a5SFrançois Tigeot 		return ksnprintf(buf, PAGE_SIZE, "off\n");
524c6f73aabSFrançois Tigeot 
52557e252bfSMichael Neumann 	return snprintf(buf, PAGE_SIZE, "%s\n",
52657e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
52757e252bfSMichael Neumann 			(level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
52857e252bfSMichael Neumann }
52957e252bfSMichael Neumann 
radeon_set_dpm_forced_performance_level(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)53057e252bfSMichael Neumann static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
53157e252bfSMichael Neumann 						       struct device_attribute *attr,
53257e252bfSMichael Neumann 						       const char *buf,
53357e252bfSMichael Neumann 						       size_t count)
53457e252bfSMichael Neumann {
535c6f73aabSFrançois Tigeot 	struct drm_device *ddev = dev_get_drvdata(dev);
53657e252bfSMichael Neumann 	struct radeon_device *rdev = ddev->dev_private;
53757e252bfSMichael Neumann 	enum radeon_dpm_forced_level level;
53857e252bfSMichael Neumann 	int ret = 0;
53957e252bfSMichael Neumann 
540c6f73aabSFrançois Tigeot 	/* Can't force performance level when the card is off */
541c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
542c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
543c6f73aabSFrançois Tigeot 		return -EINVAL;
544c6f73aabSFrançois Tigeot 
5451cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
54657e252bfSMichael Neumann 	if (strncmp("low", buf, strlen("low")) == 0) {
54757e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_LOW;
54857e252bfSMichael Neumann 	} else if (strncmp("high", buf, strlen("high")) == 0) {
54957e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_HIGH;
55057e252bfSMichael Neumann 	} else if (strncmp("auto", buf, strlen("auto")) == 0) {
55157e252bfSMichael Neumann 		level = RADEON_DPM_FORCED_LEVEL_AUTO;
55257e252bfSMichael Neumann 	} else {
55357e252bfSMichael Neumann 		count = -EINVAL;
55457e252bfSMichael Neumann 		goto fail;
55557e252bfSMichael Neumann 	}
55657e252bfSMichael Neumann 	if (rdev->asic->dpm.force_performance_level) {
557c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
558c6f73aabSFrançois Tigeot 			count = -EINVAL;
559c6f73aabSFrançois Tigeot 			goto fail;
560c6f73aabSFrançois Tigeot 		}
56157e252bfSMichael Neumann 		ret = radeon_dpm_force_performance_level(rdev, level);
56257e252bfSMichael Neumann 		if (ret)
56357e252bfSMichael Neumann 			count = -EINVAL;
56457e252bfSMichael Neumann 	}
56557e252bfSMichael Neumann fail:
5661cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
567c6f73aabSFrançois Tigeot 
56857e252bfSMichael Neumann 	return count;
56957e252bfSMichael Neumann }
57057e252bfSMichael Neumann 
radeon_hwmon_get_pwm1_enable(struct device * dev,struct device_attribute * attr,char * buf)571c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev,
572c59a5c48SFrançois Tigeot 					    struct device_attribute *attr,
573c59a5c48SFrançois Tigeot 					    char *buf)
574c59a5c48SFrançois Tigeot {
575c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
576c59a5c48SFrançois Tigeot 	u32 pwm_mode = 0;
577c59a5c48SFrançois Tigeot 
578c59a5c48SFrançois Tigeot 	if (rdev->asic->dpm.fan_ctrl_get_mode)
579c59a5c48SFrançois Tigeot 		pwm_mode = rdev->asic->dpm.fan_ctrl_get_mode(rdev);
580c59a5c48SFrançois Tigeot 
581c59a5c48SFrançois Tigeot 	/* never 0 (full-speed), fuse or smc-controlled always */
582c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2);
583c59a5c48SFrançois Tigeot }
584c59a5c48SFrançois Tigeot 
radeon_hwmon_set_pwm1_enable(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)585c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev,
586c59a5c48SFrançois Tigeot 					    struct device_attribute *attr,
587c59a5c48SFrançois Tigeot 					    const char *buf,
588c59a5c48SFrançois Tigeot 					    size_t count)
589c59a5c48SFrançois Tigeot {
590c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
591c59a5c48SFrançois Tigeot 	int err;
592c59a5c48SFrançois Tigeot 	int value;
593c59a5c48SFrançois Tigeot 
594c59a5c48SFrançois Tigeot 	if(!rdev->asic->dpm.fan_ctrl_set_mode)
595c59a5c48SFrançois Tigeot 		return -EINVAL;
596c59a5c48SFrançois Tigeot 
597c59a5c48SFrançois Tigeot 	err = kstrtoint(buf, 10, &value);
598c59a5c48SFrançois Tigeot 	if (err)
599c59a5c48SFrançois Tigeot 		return err;
600c59a5c48SFrançois Tigeot 
601c59a5c48SFrançois Tigeot 	switch (value) {
602c59a5c48SFrançois Tigeot 	case 1: /* manual, percent-based */
603c59a5c48SFrançois Tigeot 		rdev->asic->dpm.fan_ctrl_set_mode(rdev, FDO_PWM_MODE_STATIC);
604c59a5c48SFrançois Tigeot 		break;
605c59a5c48SFrançois Tigeot 	default: /* disable */
606c59a5c48SFrançois Tigeot 		rdev->asic->dpm.fan_ctrl_set_mode(rdev, 0);
607c59a5c48SFrançois Tigeot 		break;
608c59a5c48SFrançois Tigeot 	}
609c59a5c48SFrançois Tigeot 
610c59a5c48SFrançois Tigeot 	return count;
611c59a5c48SFrançois Tigeot }
612c59a5c48SFrançois Tigeot 
radeon_hwmon_get_pwm1_min(struct device * dev,struct device_attribute * attr,char * buf)613c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_min(struct device *dev,
614c59a5c48SFrançois Tigeot 					 struct device_attribute *attr,
615c59a5c48SFrançois Tigeot 					 char *buf)
616c59a5c48SFrançois Tigeot {
617c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", 0);
618c59a5c48SFrançois Tigeot }
619c59a5c48SFrançois Tigeot 
radeon_hwmon_get_pwm1_max(struct device * dev,struct device_attribute * attr,char * buf)620c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1_max(struct device *dev,
621c59a5c48SFrançois Tigeot 					 struct device_attribute *attr,
622c59a5c48SFrançois Tigeot 					 char *buf)
623c59a5c48SFrançois Tigeot {
624c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", 255);
625c59a5c48SFrançois Tigeot }
626c59a5c48SFrançois Tigeot 
radeon_hwmon_set_pwm1(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)627c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_set_pwm1(struct device *dev,
628c59a5c48SFrançois Tigeot 				     struct device_attribute *attr,
629c59a5c48SFrançois Tigeot 				     const char *buf, size_t count)
630c59a5c48SFrançois Tigeot {
631c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
632c59a5c48SFrançois Tigeot 	int err;
633c59a5c48SFrançois Tigeot 	u32 value;
634c59a5c48SFrançois Tigeot 
635c59a5c48SFrançois Tigeot 	err = kstrtou32(buf, 10, &value);
636c59a5c48SFrançois Tigeot 	if (err)
637c59a5c48SFrançois Tigeot 		return err;
638c59a5c48SFrançois Tigeot 
639c59a5c48SFrançois Tigeot 	value = (value * 100) / 255;
640c59a5c48SFrançois Tigeot 
641c59a5c48SFrançois Tigeot 	err = rdev->asic->dpm.set_fan_speed_percent(rdev, value);
642c59a5c48SFrançois Tigeot 	if (err)
643c59a5c48SFrançois Tigeot 		return err;
644c59a5c48SFrançois Tigeot 
645c59a5c48SFrançois Tigeot 	return count;
646c59a5c48SFrançois Tigeot }
647c59a5c48SFrançois Tigeot 
radeon_hwmon_get_pwm1(struct device * dev,struct device_attribute * attr,char * buf)648c59a5c48SFrançois Tigeot static ssize_t radeon_hwmon_get_pwm1(struct device *dev,
649c59a5c48SFrançois Tigeot 				     struct device_attribute *attr,
650c59a5c48SFrançois Tigeot 				     char *buf)
651c59a5c48SFrançois Tigeot {
652c59a5c48SFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
653c59a5c48SFrançois Tigeot 	int err;
654c59a5c48SFrançois Tigeot 	u32 speed;
655c59a5c48SFrançois Tigeot 
656c59a5c48SFrançois Tigeot 	err = rdev->asic->dpm.get_fan_speed_percent(rdev, &speed);
657c59a5c48SFrançois Tigeot 	if (err)
658c59a5c48SFrançois Tigeot 		return err;
659c59a5c48SFrançois Tigeot 
660c59a5c48SFrançois Tigeot 	speed = (speed * 255) / 100;
661c59a5c48SFrançois Tigeot 
662c59a5c48SFrançois Tigeot 	return sprintf(buf, "%i\n", speed);
663c59a5c48SFrançois Tigeot }
664c59a5c48SFrançois Tigeot 
665926deccbSFrançois Tigeot static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
666926deccbSFrançois Tigeot static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
66757e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
66857e252bfSMichael Neumann static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
66957e252bfSMichael Neumann 		   radeon_get_dpm_forced_performance_level,
67057e252bfSMichael Neumann 		   radeon_set_dpm_forced_performance_level);
671926deccbSFrançois Tigeot 
radeon_hwmon_show_temp(struct device * dev,struct device_attribute * attr,char * buf)672926deccbSFrançois Tigeot static ssize_t radeon_hwmon_show_temp(struct device *dev,
673926deccbSFrançois Tigeot 				      struct device_attribute *attr,
674926deccbSFrançois Tigeot 				      char *buf)
675926deccbSFrançois Tigeot {
676c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
677c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
678926deccbSFrançois Tigeot 	int temp;
679926deccbSFrançois Tigeot 
680c6f73aabSFrançois Tigeot 	/* Can't get temperature when the card is off */
681c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
682c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
683c6f73aabSFrançois Tigeot 		return -EINVAL;
684c6f73aabSFrançois Tigeot 
68557e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature)
68657e252bfSMichael Neumann 		temp = radeon_get_temperature(rdev);
68757e252bfSMichael Neumann 	else
688926deccbSFrançois Tigeot 		temp = 0;
689926deccbSFrançois Tigeot 
690926deccbSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
691926deccbSFrançois Tigeot }
692926deccbSFrançois Tigeot 
radeon_hwmon_show_temp_thresh(struct device * dev,struct device_attribute * attr,char * buf)693c6f73aabSFrançois Tigeot static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev,
694926deccbSFrançois Tigeot 					     struct device_attribute *attr,
695926deccbSFrançois Tigeot 					     char *buf)
696926deccbSFrançois Tigeot {
697c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
698c6f73aabSFrançois Tigeot 	int hyst = to_sensor_dev_attr(attr)->index;
699c6f73aabSFrançois Tigeot 	int temp;
700c6f73aabSFrançois Tigeot 
701c6f73aabSFrançois Tigeot 	if (hyst)
702c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.min_temp;
703c6f73aabSFrançois Tigeot 	else
704c6f73aabSFrançois Tigeot 		temp = rdev->pm.dpm.thermal.max_temp;
705c6f73aabSFrançois Tigeot 
706c6f73aabSFrançois Tigeot 	return ksnprintf(buf, PAGE_SIZE, "%d\n", temp);
707926deccbSFrançois Tigeot }
708926deccbSFrançois Tigeot 
709926deccbSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0);
710c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0);
711c6f73aabSFrançois Tigeot static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1);
712c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0);
713c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0);
714c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0);
715c59a5c48SFrançois Tigeot static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0);
716c59a5c48SFrançois Tigeot 
717926deccbSFrançois Tigeot 
718926deccbSFrançois Tigeot static struct attribute *hwmon_attributes[] = {
719926deccbSFrançois Tigeot 	&sensor_dev_attr_temp1_input.dev_attr.attr,
720c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
721c6f73aabSFrançois Tigeot 	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
722c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1.dev_attr.attr,
723c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
724c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_min.dev_attr.attr,
725c59a5c48SFrançois Tigeot 	&sensor_dev_attr_pwm1_max.dev_attr.attr,
726926deccbSFrançois Tigeot 	NULL
727926deccbSFrançois Tigeot };
728926deccbSFrançois Tigeot 
hwmon_attributes_visible(struct kobject * kobj,struct attribute * attr,int index)729c6f73aabSFrançois Tigeot static umode_t hwmon_attributes_visible(struct kobject *kobj,
730c6f73aabSFrançois Tigeot 					struct attribute *attr, int index)
731c6f73aabSFrançois Tigeot {
732d78d3a22SFrançois Tigeot 	struct device *dev = kobj_to_dev(kobj);
733c6f73aabSFrançois Tigeot 	struct radeon_device *rdev = dev_get_drvdata(dev);
734c59a5c48SFrançois Tigeot 	umode_t effective_mode = attr->mode;
735c6f73aabSFrançois Tigeot 
736c59a5c48SFrançois Tigeot 	/* Skip attributes if DPM is not enabled */
737c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method != PM_METHOD_DPM &&
738c6f73aabSFrançois Tigeot 	    (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr ||
739c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr ||
740c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr ||
741c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr ||
742c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
743c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
744c6f73aabSFrançois Tigeot 		return 0;
745c6f73aabSFrançois Tigeot 
746c59a5c48SFrançois Tigeot 	/* Skip fan attributes if fan is not present */
747c59a5c48SFrançois Tigeot 	if (rdev->pm.no_fan &&
748c59a5c48SFrançois Tigeot 	    (attr == &sensor_dev_attr_pwm1.dev_attr.attr ||
749c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr ||
750c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
751c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
752c59a5c48SFrançois Tigeot 		return 0;
753c59a5c48SFrançois Tigeot 
754c59a5c48SFrançois Tigeot 	/* mask fan attributes if we have no bindings for this asic to expose */
755c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.get_fan_speed_percent &&
756c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */
757c59a5c48SFrançois Tigeot 	    (!rdev->asic->dpm.fan_ctrl_get_mode &&
758c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */
759c59a5c48SFrançois Tigeot 		effective_mode &= ~S_IRUGO;
760c59a5c48SFrançois Tigeot 
761c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.set_fan_speed_percent &&
762c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */
763c59a5c48SFrançois Tigeot 	    (!rdev->asic->dpm.fan_ctrl_set_mode &&
764c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */
765c59a5c48SFrançois Tigeot 		effective_mode &= ~S_IWUSR;
766c59a5c48SFrançois Tigeot 
767c59a5c48SFrançois Tigeot 	/* hide max/min values if we can't both query and manage the fan */
768c59a5c48SFrançois Tigeot 	if ((!rdev->asic->dpm.set_fan_speed_percent &&
769c59a5c48SFrançois Tigeot 	     !rdev->asic->dpm.get_fan_speed_percent) &&
770c59a5c48SFrançois Tigeot 	    (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr ||
771c59a5c48SFrançois Tigeot 	     attr == &sensor_dev_attr_pwm1_min.dev_attr.attr))
772c59a5c48SFrançois Tigeot 		return 0;
773c59a5c48SFrançois Tigeot 
774c59a5c48SFrançois Tigeot 	return effective_mode;
775c6f73aabSFrançois Tigeot }
776c6f73aabSFrançois Tigeot 
777926deccbSFrançois Tigeot static const struct attribute_group hwmon_attrgroup = {
778926deccbSFrançois Tigeot 	.attrs = hwmon_attributes,
779c6f73aabSFrançois Tigeot 	.is_visible = hwmon_attributes_visible,
780c6f73aabSFrançois Tigeot };
781c6f73aabSFrançois Tigeot 
782c6f73aabSFrançois Tigeot static const struct attribute_group *hwmon_groups[] = {
783c6f73aabSFrançois Tigeot 	&hwmon_attrgroup,
784c6f73aabSFrançois Tigeot 	NULL
785926deccbSFrançois Tigeot };
786926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
787926deccbSFrançois Tigeot 
7882c86cb5bSImre Vadász static void
radeon_hwmon_refresh(void * arg)7892c86cb5bSImre Vadász radeon_hwmon_refresh(void *arg)
7902c86cb5bSImre Vadász {
7912c86cb5bSImre Vadász 	struct radeon_device *rdev = (struct radeon_device *)arg;
79226b5dbf2SImre Vadász 	struct drm_device *ddev = rdev->ddev;
7932c86cb5bSImre Vadász 	struct ksensor *s = rdev->pm.int_sensor;
7942c86cb5bSImre Vadász 	int temp;
79526b5dbf2SImre Vadász 	enum sensor_status stat;
7962c86cb5bSImre Vadász 
79726b5dbf2SImre Vadász 	/* Can't get temperature when the card is off */
79826b5dbf2SImre Vadász 	if  ((rdev->flags & RADEON_IS_PX) &&
79926b5dbf2SImre Vadász 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
80026b5dbf2SImre Vadász 		sensor_set_unknown(s);
80126b5dbf2SImre Vadász 		s->status = SENSOR_S_OK;
80226b5dbf2SImre Vadász 		return;
80326b5dbf2SImre Vadász 	}
80426b5dbf2SImre Vadász 
80526b5dbf2SImre Vadász 	if (rdev->asic->pm.get_temperature == NULL) {
80626b5dbf2SImre Vadász 		sensor_set_invalid(s);
80726b5dbf2SImre Vadász 		return;
80826b5dbf2SImre Vadász 	}
80926b5dbf2SImre Vadász 
8102c86cb5bSImre Vadász 	temp = radeon_get_temperature(rdev);
81126b5dbf2SImre Vadász 	if (temp >= rdev->pm.dpm.thermal.max_temp)
81226b5dbf2SImre Vadász 		stat = SENSOR_S_CRIT;
81326b5dbf2SImre Vadász 	else if (temp >= rdev->pm.dpm.thermal.min_temp)
81426b5dbf2SImre Vadász 		stat = SENSOR_S_WARN;
8152c86cb5bSImre Vadász 	else
81626b5dbf2SImre Vadász 		stat = SENSOR_S_OK;
8172c86cb5bSImre Vadász 
81826b5dbf2SImre Vadász 	sensor_set(s, temp * 1000 + 273150000, stat);
8192c86cb5bSImre Vadász }
8202c86cb5bSImre Vadász 
radeon_hwmon_init(struct radeon_device * rdev)821926deccbSFrançois Tigeot static int radeon_hwmon_init(struct radeon_device *rdev)
822926deccbSFrançois Tigeot {
823926deccbSFrançois Tigeot 	int err = 0;
824926deccbSFrançois Tigeot 
8252c86cb5bSImre Vadász 	rdev->pm.int_sensor = NULL;
8262c86cb5bSImre Vadász 	rdev->pm.int_sensordev = NULL;
827926deccbSFrançois Tigeot 
828926deccbSFrançois Tigeot 	switch (rdev->pm.int_thermal_type) {
829926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV6XX:
830926deccbSFrançois Tigeot 	case THERMAL_TYPE_RV770:
831926deccbSFrançois Tigeot 	case THERMAL_TYPE_EVERGREEN:
832926deccbSFrançois Tigeot 	case THERMAL_TYPE_NI:
833926deccbSFrançois Tigeot 	case THERMAL_TYPE_SUMO:
834926deccbSFrançois Tigeot 	case THERMAL_TYPE_SI:
8354cd92098Szrj 	case THERMAL_TYPE_CI:
8364cd92098Szrj 	case THERMAL_TYPE_KV:
83757e252bfSMichael Neumann 		if (rdev->asic->pm.get_temperature == NULL)
838926deccbSFrançois Tigeot 			return err;
8392c86cb5bSImre Vadász 
8402c86cb5bSImre Vadász 		rdev->pm.int_sensor = kmalloc(sizeof(*rdev->pm.int_sensor),
8412c86cb5bSImre Vadász 		    M_DRM, M_ZERO | M_WAITOK);
8422c86cb5bSImre Vadász 		rdev->pm.int_sensordev = kmalloc(
8432c86cb5bSImre Vadász 		    sizeof(*rdev->pm.int_sensordev), M_DRM,
8442c86cb5bSImre Vadász 		    M_ZERO | M_WAITOK);
8452c86cb5bSImre Vadász 		strlcpy(rdev->pm.int_sensordev->xname,
846fb572d17SFrançois Tigeot 		    device_get_nameunit(rdev->dev->bsddev),
8472c86cb5bSImre Vadász 		    sizeof(rdev->pm.int_sensordev->xname));
8482c86cb5bSImre Vadász 		rdev->pm.int_sensor->type = SENSOR_TEMP;
84926b5dbf2SImre Vadász 		rdev->pm.int_sensor->flags |= SENSOR_FINVALID;
8502c86cb5bSImre Vadász 		sensor_attach(rdev->pm.int_sensordev, rdev->pm.int_sensor);
8512c86cb5bSImre Vadász 		sensor_task_register(rdev, radeon_hwmon_refresh, 5);
8522c86cb5bSImre Vadász 		sensordev_install(rdev->pm.int_sensordev);
853926deccbSFrançois Tigeot 		break;
854926deccbSFrançois Tigeot 	default:
855926deccbSFrançois Tigeot 		break;
856926deccbSFrançois Tigeot 	}
857926deccbSFrançois Tigeot 
858926deccbSFrançois Tigeot 	return err;
859926deccbSFrançois Tigeot }
860926deccbSFrançois Tigeot 
radeon_hwmon_fini(struct radeon_device * rdev)861926deccbSFrançois Tigeot static void radeon_hwmon_fini(struct radeon_device *rdev)
862926deccbSFrançois Tigeot {
8632c86cb5bSImre Vadász 	if (rdev->pm.int_sensor != NULL && rdev->pm.int_sensordev != NULL) {
8642c86cb5bSImre Vadász 		sensordev_deinstall(rdev->pm.int_sensordev);
8652c86cb5bSImre Vadász 		sensor_task_unregister(rdev);
8662c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensor);
8672c86cb5bSImre Vadász 		kfree(rdev->pm.int_sensordev);
8682c86cb5bSImre Vadász 		rdev->pm.int_sensor = NULL;
8692c86cb5bSImre Vadász 		rdev->pm.int_sensordev = NULL;
870926deccbSFrançois Tigeot 	}
871926deccbSFrançois Tigeot }
872926deccbSFrançois Tigeot 
radeon_dpm_thermal_work_handler(struct work_struct * work)8732c5cc6b9SFrançois Tigeot static void radeon_dpm_thermal_work_handler(struct work_struct *work)
87457e252bfSMichael Neumann {
8752c5cc6b9SFrançois Tigeot 	struct radeon_device *rdev =
8762c5cc6b9SFrançois Tigeot 		container_of(work, struct radeon_device,
8772c5cc6b9SFrançois Tigeot 			     pm.dpm.thermal.work);
87857e252bfSMichael Neumann 	/* switch to the thermal state */
87957e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
88057e252bfSMichael Neumann 
88157e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
88257e252bfSMichael Neumann 		return;
88357e252bfSMichael Neumann 
88457e252bfSMichael Neumann 	if (rdev->asic->pm.get_temperature) {
88557e252bfSMichael Neumann 		int temp = radeon_get_temperature(rdev);
88657e252bfSMichael Neumann 
88757e252bfSMichael Neumann 		if (temp < rdev->pm.dpm.thermal.min_temp)
88857e252bfSMichael Neumann 			/* switch back the user state */
88957e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
89057e252bfSMichael Neumann 	} else {
89157e252bfSMichael Neumann 		if (rdev->pm.dpm.thermal.high_to_low)
89257e252bfSMichael Neumann 			/* switch back the user state */
89357e252bfSMichael Neumann 			dpm_state = rdev->pm.dpm.user_state;
89457e252bfSMichael Neumann 	}
8954cd92098Szrj 	mutex_lock(&rdev->pm.mutex);
8964cd92098Szrj 	if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL)
8974cd92098Szrj 		rdev->pm.dpm.thermal_active = true;
8984cd92098Szrj 	else
8994cd92098Szrj 		rdev->pm.dpm.thermal_active = false;
9004cd92098Szrj 	rdev->pm.dpm.state = dpm_state;
9014cd92098Szrj 	mutex_unlock(&rdev->pm.mutex);
9024cd92098Szrj 
9034cd92098Szrj 	radeon_pm_compute_clocks(rdev);
90457e252bfSMichael Neumann }
90557e252bfSMichael Neumann 
radeon_dpm_single_display(struct radeon_device * rdev)9067dcf36dcSFrançois Tigeot static bool radeon_dpm_single_display(struct radeon_device *rdev)
90757e252bfSMichael Neumann {
90857e252bfSMichael Neumann 	bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
90957e252bfSMichael Neumann 		true : false;
91057e252bfSMichael Neumann 
91157e252bfSMichael Neumann 	/* check if the vblank period is too short to adjust the mclk */
91257e252bfSMichael Neumann 	if (single_display && rdev->asic->dpm.vblank_too_short) {
91357e252bfSMichael Neumann 		if (radeon_dpm_vblank_too_short(rdev))
91457e252bfSMichael Neumann 			single_display = false;
91557e252bfSMichael Neumann 	}
91657e252bfSMichael Neumann 
9177dcf36dcSFrançois Tigeot 	return single_display;
9187dcf36dcSFrançois Tigeot }
9197dcf36dcSFrançois Tigeot 
radeon_dpm_pick_power_state(struct radeon_device * rdev,enum radeon_pm_state_type dpm_state)9207dcf36dcSFrançois Tigeot static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
9217dcf36dcSFrançois Tigeot 						     enum radeon_pm_state_type dpm_state)
9227dcf36dcSFrançois Tigeot {
9237dcf36dcSFrançois Tigeot 	int i;
9247dcf36dcSFrançois Tigeot 	struct radeon_ps *ps;
9257dcf36dcSFrançois Tigeot 	u32 ui_class;
9267dcf36dcSFrançois Tigeot 	bool single_display = radeon_dpm_single_display(rdev);
9277dcf36dcSFrançois Tigeot 
928c59a5c48SFrançois Tigeot 	/* 120hz tends to be problematic even if they are under the
929c59a5c48SFrançois Tigeot 	 * vblank limit.
930c59a5c48SFrançois Tigeot 	 */
931c59a5c48SFrançois Tigeot 	if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120))
932c59a5c48SFrançois Tigeot 		single_display = false;
933c59a5c48SFrançois Tigeot 
93457e252bfSMichael Neumann 	/* certain older asics have a separare 3D performance state,
93557e252bfSMichael Neumann 	 * so try that first if the user selected performance
93657e252bfSMichael Neumann 	 */
93757e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
93857e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
93957e252bfSMichael Neumann 	/* balanced states don't exist at the moment */
94057e252bfSMichael Neumann 	if (dpm_state == POWER_STATE_TYPE_BALANCED)
941*3f2dd94aSFrançois Tigeot 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
94257e252bfSMichael Neumann 
94357e252bfSMichael Neumann restart_search:
94457e252bfSMichael Neumann 	/* Pick the best power state based on current conditions */
94557e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
94657e252bfSMichael Neumann 		ps = &rdev->pm.dpm.ps[i];
94757e252bfSMichael Neumann 		ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
94857e252bfSMichael Neumann 		switch (dpm_state) {
94957e252bfSMichael Neumann 		/* user states */
95057e252bfSMichael Neumann 		case POWER_STATE_TYPE_BATTERY:
95157e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
95257e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
95357e252bfSMichael Neumann 					if (single_display)
95457e252bfSMichael Neumann 						return ps;
95557e252bfSMichael Neumann 				} else
95657e252bfSMichael Neumann 					return ps;
95757e252bfSMichael Neumann 			}
95857e252bfSMichael Neumann 			break;
95957e252bfSMichael Neumann 		case POWER_STATE_TYPE_BALANCED:
96057e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
96157e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
96257e252bfSMichael Neumann 					if (single_display)
96357e252bfSMichael Neumann 						return ps;
96457e252bfSMichael Neumann 				} else
96557e252bfSMichael Neumann 					return ps;
96657e252bfSMichael Neumann 			}
96757e252bfSMichael Neumann 			break;
96857e252bfSMichael Neumann 		case POWER_STATE_TYPE_PERFORMANCE:
96957e252bfSMichael Neumann 			if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
97057e252bfSMichael Neumann 				if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
97157e252bfSMichael Neumann 					if (single_display)
97257e252bfSMichael Neumann 						return ps;
97357e252bfSMichael Neumann 				} else
97457e252bfSMichael Neumann 					return ps;
97557e252bfSMichael Neumann 			}
97657e252bfSMichael Neumann 			break;
97757e252bfSMichael Neumann 		/* internal states */
97857e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD:
9794cd92098Szrj 			if (rdev->pm.dpm.uvd_ps)
98057e252bfSMichael Neumann 				return rdev->pm.dpm.uvd_ps;
9814cd92098Szrj 			else
9824cd92098Szrj 				break;
98357e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_SD:
98457e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
98557e252bfSMichael Neumann 				return ps;
98657e252bfSMichael Neumann 			break;
98757e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD:
98857e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
98957e252bfSMichael Neumann 				return ps;
99057e252bfSMichael Neumann 			break;
99157e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
99257e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
99357e252bfSMichael Neumann 				return ps;
99457e252bfSMichael Neumann 			break;
99557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
99657e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
99757e252bfSMichael Neumann 				return ps;
99857e252bfSMichael Neumann 			break;
99957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_BOOT:
100057e252bfSMichael Neumann 			return rdev->pm.dpm.boot_ps;
100157e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_THERMAL:
100257e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
100357e252bfSMichael Neumann 				return ps;
100457e252bfSMichael Neumann 			break;
100557e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ACPI:
100657e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
100757e252bfSMichael Neumann 				return ps;
100857e252bfSMichael Neumann 			break;
100957e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_ULV:
101057e252bfSMichael Neumann 			if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
101157e252bfSMichael Neumann 				return ps;
101257e252bfSMichael Neumann 			break;
101357e252bfSMichael Neumann 		case POWER_STATE_TYPE_INTERNAL_3DPERF:
101457e252bfSMichael Neumann 			if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
101557e252bfSMichael Neumann 				return ps;
101657e252bfSMichael Neumann 			break;
101757e252bfSMichael Neumann 		default:
101857e252bfSMichael Neumann 			break;
101957e252bfSMichael Neumann 		}
102057e252bfSMichael Neumann 	}
102157e252bfSMichael Neumann 	/* use a fallback state if we didn't match */
102257e252bfSMichael Neumann 	switch (dpm_state) {
102357e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_SD:
10244cd92098Szrj 		dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
10254cd92098Szrj 		goto restart_search;
102657e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD:
102757e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
102857e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
10294cd92098Szrj 		if (rdev->pm.dpm.uvd_ps) {
103057e252bfSMichael Neumann 			return rdev->pm.dpm.uvd_ps;
10314cd92098Szrj 		} else {
10324cd92098Szrj 			dpm_state = POWER_STATE_TYPE_PERFORMANCE;
10334cd92098Szrj 			goto restart_search;
10344cd92098Szrj 		}
103557e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_THERMAL:
103657e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
103757e252bfSMichael Neumann 		goto restart_search;
103857e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_ACPI:
103957e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_BATTERY;
104057e252bfSMichael Neumann 		goto restart_search;
104157e252bfSMichael Neumann 	case POWER_STATE_TYPE_BATTERY:
104257e252bfSMichael Neumann 	case POWER_STATE_TYPE_BALANCED:
104357e252bfSMichael Neumann 	case POWER_STATE_TYPE_INTERNAL_3DPERF:
104457e252bfSMichael Neumann 		dpm_state = POWER_STATE_TYPE_PERFORMANCE;
104557e252bfSMichael Neumann 		goto restart_search;
104657e252bfSMichael Neumann 	default:
104757e252bfSMichael Neumann 		break;
104857e252bfSMichael Neumann 	}
104957e252bfSMichael Neumann 
105057e252bfSMichael Neumann 	return NULL;
105157e252bfSMichael Neumann }
105257e252bfSMichael Neumann 
radeon_dpm_change_power_state_locked(struct radeon_device * rdev)105357e252bfSMichael Neumann static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
105457e252bfSMichael Neumann {
105557e252bfSMichael Neumann 	int i;
105657e252bfSMichael Neumann 	struct radeon_ps *ps;
105757e252bfSMichael Neumann 	enum radeon_pm_state_type dpm_state;
105857e252bfSMichael Neumann 	int ret;
10597dcf36dcSFrançois Tigeot 	bool single_display = radeon_dpm_single_display(rdev);
106057e252bfSMichael Neumann 
106157e252bfSMichael Neumann 	/* if dpm init failed */
106257e252bfSMichael Neumann 	if (!rdev->pm.dpm_enabled)
106357e252bfSMichael Neumann 		return;
106457e252bfSMichael Neumann 
106557e252bfSMichael Neumann 	if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
106657e252bfSMichael Neumann 		/* add other state override checks here */
106757e252bfSMichael Neumann 		if ((!rdev->pm.dpm.thermal_active) &&
106857e252bfSMichael Neumann 		    (!rdev->pm.dpm.uvd_active))
106957e252bfSMichael Neumann 			rdev->pm.dpm.state = rdev->pm.dpm.user_state;
107057e252bfSMichael Neumann 	}
107157e252bfSMichael Neumann 	dpm_state = rdev->pm.dpm.state;
107257e252bfSMichael Neumann 
107357e252bfSMichael Neumann 	ps = radeon_dpm_pick_power_state(rdev, dpm_state);
107457e252bfSMichael Neumann 	if (ps)
107557e252bfSMichael Neumann 		rdev->pm.dpm.requested_ps = ps;
107657e252bfSMichael Neumann 	else
107757e252bfSMichael Neumann 		return;
107857e252bfSMichael Neumann 
107957e252bfSMichael Neumann 	/* no need to reprogram if nothing changed unless we are on BTC+ */
108057e252bfSMichael Neumann 	if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
1081c6f73aabSFrançois Tigeot 		/* vce just modifies an existing state so force a change */
1082c6f73aabSFrançois Tigeot 		if (ps->vce_active != rdev->pm.dpm.vce_active)
1083c6f73aabSFrançois Tigeot 			goto force;
10847dcf36dcSFrançois Tigeot 		/* user has made a display change (such as timing) */
10857dcf36dcSFrançois Tigeot 		if (rdev->pm.dpm.single_display != single_display)
10867dcf36dcSFrançois Tigeot 			goto force;
108757e252bfSMichael Neumann 		if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
108857e252bfSMichael Neumann 			/* for pre-BTC and APUs if the num crtcs changed but state is the same,
108957e252bfSMichael Neumann 			 * all we need to do is update the display configuration.
109057e252bfSMichael Neumann 			 */
109157e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
109257e252bfSMichael Neumann 				/* update display watermarks based on new power state */
109357e252bfSMichael Neumann 				radeon_bandwidth_update(rdev);
109457e252bfSMichael Neumann 				/* update displays */
109557e252bfSMichael Neumann 				radeon_dpm_display_configuration_changed(rdev);
109657e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
109757e252bfSMichael Neumann 				rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
109857e252bfSMichael Neumann 			}
109957e252bfSMichael Neumann 			return;
110057e252bfSMichael Neumann 		} else {
110157e252bfSMichael Neumann 			/* for BTC+ if the num crtcs hasn't changed and state is the same,
110257e252bfSMichael Neumann 			 * nothing to do, if the num crtcs is > 1 and state is the same,
110357e252bfSMichael Neumann 			 * update display configuration.
110457e252bfSMichael Neumann 			 */
110557e252bfSMichael Neumann 			if (rdev->pm.dpm.new_active_crtcs ==
110657e252bfSMichael Neumann 			    rdev->pm.dpm.current_active_crtcs) {
110757e252bfSMichael Neumann 				return;
110857e252bfSMichael Neumann 			} else {
110957e252bfSMichael Neumann 				if ((rdev->pm.dpm.current_active_crtc_count > 1) &&
111057e252bfSMichael Neumann 				    (rdev->pm.dpm.new_active_crtc_count > 1)) {
111157e252bfSMichael Neumann 					/* update display watermarks based on new power state */
111257e252bfSMichael Neumann 					radeon_bandwidth_update(rdev);
111357e252bfSMichael Neumann 					/* update displays */
111457e252bfSMichael Neumann 					radeon_dpm_display_configuration_changed(rdev);
111557e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
111657e252bfSMichael Neumann 					rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
111757e252bfSMichael Neumann 					return;
111857e252bfSMichael Neumann 				}
111957e252bfSMichael Neumann 			}
112057e252bfSMichael Neumann 		}
112157e252bfSMichael Neumann 	}
112257e252bfSMichael Neumann 
1123c6f73aabSFrançois Tigeot force:
1124c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1) {
112557e252bfSMichael Neumann 		printk("switching from power state:\n");
112657e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
112757e252bfSMichael Neumann 		printk("switching to power state:\n");
112857e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
1129c6f73aabSFrançois Tigeot 	}
113057e252bfSMichael Neumann 
11311cfef1a5SFrançois Tigeot 	down_write(&rdev->pm.mclk_lock);
11321cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->ring_lock);
113357e252bfSMichael Neumann 
1134c6f73aabSFrançois Tigeot 	/* update whether vce is active */
1135c6f73aabSFrançois Tigeot 	ps->vce_active = rdev->pm.dpm.vce_active;
1136c6f73aabSFrançois Tigeot 
113757e252bfSMichael Neumann 	ret = radeon_dpm_pre_set_power_state(rdev);
113857e252bfSMichael Neumann 	if (ret)
113957e252bfSMichael Neumann 		goto done;
114057e252bfSMichael Neumann 
114157e252bfSMichael Neumann 	/* update display watermarks based on new power state */
114257e252bfSMichael Neumann 	radeon_bandwidth_update(rdev);
114357e252bfSMichael Neumann 
114457e252bfSMichael Neumann 	/* wait for the rings to drain */
114557e252bfSMichael Neumann 	for (i = 0; i < RADEON_NUM_RINGS; i++) {
114657e252bfSMichael Neumann 		struct radeon_ring *ring = &rdev->ring[i];
114757e252bfSMichael Neumann 		if (ring->ready)
1148c6f73aabSFrançois Tigeot 			radeon_fence_wait_empty(rdev, i);
114957e252bfSMichael Neumann 	}
115057e252bfSMichael Neumann 
115157e252bfSMichael Neumann 	/* program the new power state */
115257e252bfSMichael Neumann 	radeon_dpm_set_power_state(rdev);
115357e252bfSMichael Neumann 
115457e252bfSMichael Neumann 	/* update current power state */
115557e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
115657e252bfSMichael Neumann 
115757e252bfSMichael Neumann 	radeon_dpm_post_set_power_state(rdev);
115857e252bfSMichael Neumann 
1159c59a5c48SFrançois Tigeot 	rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
1160c59a5c48SFrançois Tigeot 	rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
1161c59a5c48SFrançois Tigeot 	rdev->pm.dpm.single_display = single_display;
1162c59a5c48SFrançois Tigeot 
1163d78d3a22SFrançois Tigeot 	/* update displays */
1164d78d3a22SFrançois Tigeot 	radeon_dpm_display_configuration_changed(rdev);
1165d78d3a22SFrançois Tigeot 
11664cd92098Szrj 	if (rdev->asic->dpm.force_performance_level) {
1167c6f73aabSFrançois Tigeot 		if (rdev->pm.dpm.thermal_active) {
1168c6f73aabSFrançois Tigeot 			enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
11694cd92098Szrj 			/* force low perf level for thermal */
11704cd92098Szrj 			radeon_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_LOW);
1171c6f73aabSFrançois Tigeot 			/* save the user's level */
1172c6f73aabSFrançois Tigeot 			rdev->pm.dpm.forced_level = level;
1173c6f73aabSFrançois Tigeot 		} else {
1174c6f73aabSFrançois Tigeot 			/* otherwise, user selected level */
1175c6f73aabSFrançois Tigeot 			radeon_dpm_force_performance_level(rdev, rdev->pm.dpm.forced_level);
1176c6f73aabSFrançois Tigeot 		}
11774cd92098Szrj 	}
11784cd92098Szrj 
117957e252bfSMichael Neumann done:
11801cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->ring_lock);
11811cfef1a5SFrançois Tigeot 	up_write(&rdev->pm.mclk_lock);
118257e252bfSMichael Neumann }
118357e252bfSMichael Neumann 
radeon_dpm_enable_uvd(struct radeon_device * rdev,bool enable)11844cd92098Szrj void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
118557e252bfSMichael Neumann {
11864cd92098Szrj 	enum radeon_pm_state_type dpm_state;
118757e252bfSMichael Neumann 
11884cd92098Szrj 	if (rdev->asic->dpm.powergate_uvd) {
11894cd92098Szrj 		mutex_lock(&rdev->pm.mutex);
1190c6f73aabSFrançois Tigeot 		/* don't powergate anything if we
1191c6f73aabSFrançois Tigeot 		   have active but pause streams */
1192c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.sd > 0;
1193c6f73aabSFrançois Tigeot 		enable |= rdev->pm.dpm.hd > 0;
11944cd92098Szrj 		/* enable/disable UVD */
11954cd92098Szrj 		radeon_dpm_powergate_uvd(rdev, !enable);
11964cd92098Szrj 		mutex_unlock(&rdev->pm.mutex);
11974cd92098Szrj 	} else {
11984cd92098Szrj 		if (enable) {
11994cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
120057e252bfSMichael Neumann 			rdev->pm.dpm.uvd_active = true;
12014cd92098Szrj 			/* disable this for now */
12024cd92098Szrj #if 0
12034cd92098Szrj 			if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
12044cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
12054cd92098Szrj 			else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
12064cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
12074cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 1))
12084cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD;
12094cd92098Szrj 			else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
12104cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
12114cd92098Szrj 			else
12124cd92098Szrj #endif
12134cd92098Szrj 				dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
121457e252bfSMichael Neumann 			rdev->pm.dpm.state = dpm_state;
12154cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
12164cd92098Szrj 		} else {
12174cd92098Szrj 			mutex_lock(&rdev->pm.mutex);
12184cd92098Szrj 			rdev->pm.dpm.uvd_active = false;
12194cd92098Szrj 			mutex_unlock(&rdev->pm.mutex);
12204cd92098Szrj 		}
12214cd92098Szrj 
122257e252bfSMichael Neumann 		radeon_pm_compute_clocks(rdev);
122357e252bfSMichael Neumann 	}
12244cd92098Szrj }
122557e252bfSMichael Neumann 
radeon_dpm_enable_vce(struct radeon_device * rdev,bool enable)1226c6f73aabSFrançois Tigeot void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable)
1227c6f73aabSFrançois Tigeot {
1228c6f73aabSFrançois Tigeot 	if (enable) {
1229c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1230c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = true;
1231c6f73aabSFrançois Tigeot 		/* XXX select vce level based on ring/task */
1232c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL;
1233c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1234c6f73aabSFrançois Tigeot 	} else {
1235c6f73aabSFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1236c6f73aabSFrançois Tigeot 		rdev->pm.dpm.vce_active = false;
1237c6f73aabSFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1238c6f73aabSFrançois Tigeot 	}
1239c6f73aabSFrançois Tigeot 
1240c6f73aabSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1241c6f73aabSFrançois Tigeot }
1242c6f73aabSFrançois Tigeot 
radeon_pm_suspend_old(struct radeon_device * rdev)124357e252bfSMichael Neumann static void radeon_pm_suspend_old(struct radeon_device *rdev)
1244926deccbSFrançois Tigeot {
12451cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1246926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1247926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE)
1248926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED;
1249926deccbSFrançois Tigeot 	}
12501cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1251926deccbSFrançois Tigeot 
1252ee479021SImre Vadász #ifdef DUMBBELL_WIP
1253926deccbSFrançois Tigeot 	cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1254ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1255926deccbSFrançois Tigeot }
1256926deccbSFrançois Tigeot 
radeon_pm_suspend_dpm(struct radeon_device * rdev)125757e252bfSMichael Neumann static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
125857e252bfSMichael Neumann {
12591cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
126057e252bfSMichael Neumann 	/* disable dpm */
126157e252bfSMichael Neumann 	radeon_dpm_disable(rdev);
126257e252bfSMichael Neumann 	/* reset the power state */
126357e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
126457e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
12651cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
126657e252bfSMichael Neumann }
126757e252bfSMichael Neumann 
radeon_pm_suspend(struct radeon_device * rdev)126857e252bfSMichael Neumann void radeon_pm_suspend(struct radeon_device *rdev)
126957e252bfSMichael Neumann {
127057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
127157e252bfSMichael Neumann 		radeon_pm_suspend_dpm(rdev);
127257e252bfSMichael Neumann 	else
127357e252bfSMichael Neumann 		radeon_pm_suspend_old(rdev);
127457e252bfSMichael Neumann }
127557e252bfSMichael Neumann 
radeon_pm_resume_old(struct radeon_device * rdev)127657e252bfSMichael Neumann static void radeon_pm_resume_old(struct radeon_device *rdev)
1277926deccbSFrançois Tigeot {
1278926deccbSFrançois Tigeot 	/* set up the default clocks if the MC ucode is loaded */
1279926deccbSFrançois Tigeot 	if ((rdev->family >= CHIP_BARTS) &&
12804cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
1281926deccbSFrançois Tigeot 	    rdev->mc_fw) {
1282926deccbSFrançois Tigeot 		if (rdev->pm.default_vddc)
1283926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1284926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDC);
1285926deccbSFrançois Tigeot 		if (rdev->pm.default_vddci)
1286926deccbSFrançois Tigeot 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1287926deccbSFrançois Tigeot 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
1288926deccbSFrançois Tigeot 		if (rdev->pm.default_sclk)
1289926deccbSFrançois Tigeot 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1290926deccbSFrançois Tigeot 		if (rdev->pm.default_mclk)
1291926deccbSFrançois Tigeot 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1292926deccbSFrançois Tigeot 	}
1293926deccbSFrançois Tigeot 	/* asic init will reset the default power state */
12941cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1295926deccbSFrançois Tigeot 	rdev->pm.current_power_state_index = rdev->pm.default_power_state_index;
1296926deccbSFrançois Tigeot 	rdev->pm.current_clock_mode_index = 0;
1297926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->pm.default_sclk;
1298926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->pm.default_mclk;
1299c6f73aabSFrançois Tigeot 	if (rdev->pm.power_state) {
1300926deccbSFrançois Tigeot 		rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
1301926deccbSFrançois Tigeot 		rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
1302c6f73aabSFrançois Tigeot 	}
1303926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DYNPM
1304926deccbSFrançois Tigeot 	    && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
1305926deccbSFrançois Tigeot 		rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1306ee479021SImre Vadász #ifdef DUMBBELL_WIP
1307926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1308926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1309ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1310926deccbSFrançois Tigeot 	}
13111cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1312926deccbSFrançois Tigeot 	radeon_pm_compute_clocks(rdev);
1313926deccbSFrançois Tigeot }
1314926deccbSFrançois Tigeot 
radeon_pm_resume_dpm(struct radeon_device * rdev)131557e252bfSMichael Neumann static void radeon_pm_resume_dpm(struct radeon_device *rdev)
1316926deccbSFrançois Tigeot {
1317926deccbSFrançois Tigeot 	int ret;
1318926deccbSFrançois Tigeot 
131957e252bfSMichael Neumann 	/* asic init will reset to the boot state */
13201cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
132157e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
132257e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
132357e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
13241cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1325c6f73aabSFrançois Tigeot 	if (ret)
1326c6f73aabSFrançois Tigeot 		goto dpm_resume_fail;
1327c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1328c6f73aabSFrançois Tigeot 	return;
1329c6f73aabSFrançois Tigeot 
1330c6f73aabSFrançois Tigeot dpm_resume_fail:
133157e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm resume failed\n");
133257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
13334cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
133457e252bfSMichael Neumann 	    rdev->mc_fw) {
133557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
133657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
133757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
133857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
133957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
134057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
134157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
134257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
134357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
134457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
134557e252bfSMichael Neumann 	}
134657e252bfSMichael Neumann }
134757e252bfSMichael Neumann 
radeon_pm_resume(struct radeon_device * rdev)134857e252bfSMichael Neumann void radeon_pm_resume(struct radeon_device *rdev)
134957e252bfSMichael Neumann {
135057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
135157e252bfSMichael Neumann 		radeon_pm_resume_dpm(rdev);
135257e252bfSMichael Neumann 	else
135357e252bfSMichael Neumann 		radeon_pm_resume_old(rdev);
135457e252bfSMichael Neumann }
135557e252bfSMichael Neumann 
radeon_pm_init_old(struct radeon_device * rdev)135657e252bfSMichael Neumann static int radeon_pm_init_old(struct radeon_device *rdev)
135757e252bfSMichael Neumann {
135857e252bfSMichael Neumann 	int ret;
135957e252bfSMichael Neumann 
1360926deccbSFrançois Tigeot 	rdev->pm.profile = PM_PROFILE_DEFAULT;
1361926deccbSFrançois Tigeot 	rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1362926deccbSFrançois Tigeot 	rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1363926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_upclock = true;
1364926deccbSFrançois Tigeot 	rdev->pm.dynpm_can_downclock = true;
1365926deccbSFrançois Tigeot 	rdev->pm.default_sclk = rdev->clock.default_sclk;
1366926deccbSFrançois Tigeot 	rdev->pm.default_mclk = rdev->clock.default_mclk;
1367926deccbSFrançois Tigeot 	rdev->pm.current_sclk = rdev->clock.default_sclk;
1368926deccbSFrançois Tigeot 	rdev->pm.current_mclk = rdev->clock.default_mclk;
1369926deccbSFrançois Tigeot 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
1370926deccbSFrançois Tigeot 
1371926deccbSFrançois Tigeot 	if (rdev->bios) {
1372926deccbSFrançois Tigeot 		if (rdev->is_atom_bios)
1373926deccbSFrançois Tigeot 			radeon_atombios_get_power_modes(rdev);
1374926deccbSFrançois Tigeot 		else
1375926deccbSFrançois Tigeot 			radeon_combios_get_power_modes(rdev);
1376926deccbSFrançois Tigeot 		radeon_pm_print_states(rdev);
1377926deccbSFrançois Tigeot 		radeon_pm_init_profile(rdev);
1378926deccbSFrançois Tigeot 		/* set up the default clocks if the MC ucode is loaded */
1379926deccbSFrançois Tigeot 		if ((rdev->family >= CHIP_BARTS) &&
13804cd92098Szrj 		    (rdev->family <= CHIP_CAYMAN) &&
1381926deccbSFrançois Tigeot 		    rdev->mc_fw) {
1382926deccbSFrançois Tigeot 			if (rdev->pm.default_vddc)
1383926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
1384926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDC);
1385926deccbSFrançois Tigeot 			if (rdev->pm.default_vddci)
1386926deccbSFrançois Tigeot 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
1387926deccbSFrançois Tigeot 							SET_VOLTAGE_TYPE_ASIC_VDDCI);
1388926deccbSFrançois Tigeot 			if (rdev->pm.default_sclk)
1389926deccbSFrançois Tigeot 				radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
1390926deccbSFrançois Tigeot 			if (rdev->pm.default_mclk)
1391926deccbSFrançois Tigeot 				radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
1392926deccbSFrançois Tigeot 		}
1393926deccbSFrançois Tigeot 	}
1394926deccbSFrançois Tigeot 
1395926deccbSFrançois Tigeot 	/* set up the internal thermal sensor if applicable */
1396926deccbSFrançois Tigeot 	ret = radeon_hwmon_init(rdev);
1397926deccbSFrançois Tigeot 	if (ret)
1398926deccbSFrançois Tigeot 		return ret;
1399926deccbSFrançois Tigeot 
1400926deccbSFrançois Tigeot 	INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler);
1401926deccbSFrançois Tigeot 
1402926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
1403926deccbSFrançois Tigeot 		if (radeon_debugfs_pm_init(rdev)) {
1404926deccbSFrançois Tigeot 			DRM_ERROR("Failed to register debugfs file for PM!\n");
1405926deccbSFrançois Tigeot 		}
1406926deccbSFrançois Tigeot 
1407926deccbSFrançois Tigeot 		DRM_INFO("radeon: power management initialized\n");
1408926deccbSFrançois Tigeot 	}
1409926deccbSFrançois Tigeot 
1410926deccbSFrançois Tigeot 	return 0;
1411926deccbSFrançois Tigeot }
1412926deccbSFrançois Tigeot 
radeon_dpm_print_power_states(struct radeon_device * rdev)141357e252bfSMichael Neumann static void radeon_dpm_print_power_states(struct radeon_device *rdev)
141457e252bfSMichael Neumann {
141557e252bfSMichael Neumann 	int i;
141657e252bfSMichael Neumann 
141757e252bfSMichael Neumann 	for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
141857e252bfSMichael Neumann 		printk("== power state %d ==\n", i);
141957e252bfSMichael Neumann 		radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
142057e252bfSMichael Neumann 	}
142157e252bfSMichael Neumann }
142257e252bfSMichael Neumann 
radeon_pm_init_dpm(struct radeon_device * rdev)142357e252bfSMichael Neumann static int radeon_pm_init_dpm(struct radeon_device *rdev)
142457e252bfSMichael Neumann {
142557e252bfSMichael Neumann 	int ret;
142657e252bfSMichael Neumann 
14274cd92098Szrj 	/* default to balanced state */
142857e252bfSMichael Neumann 	rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
142957e252bfSMichael Neumann 	rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
14304cd92098Szrj 	rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
143157e252bfSMichael Neumann 	rdev->pm.default_sclk = rdev->clock.default_sclk;
143257e252bfSMichael Neumann 	rdev->pm.default_mclk = rdev->clock.default_mclk;
143357e252bfSMichael Neumann 	rdev->pm.current_sclk = rdev->clock.default_sclk;
143457e252bfSMichael Neumann 	rdev->pm.current_mclk = rdev->clock.default_mclk;
143557e252bfSMichael Neumann 	rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
143657e252bfSMichael Neumann 
143757e252bfSMichael Neumann 	if (rdev->bios && rdev->is_atom_bios)
143857e252bfSMichael Neumann 		radeon_atombios_get_power_modes(rdev);
143957e252bfSMichael Neumann 	else
144057e252bfSMichael Neumann 		return -EINVAL;
144157e252bfSMichael Neumann 
144257e252bfSMichael Neumann 	/* set up the internal thermal sensor if applicable */
144357e252bfSMichael Neumann 	ret = radeon_hwmon_init(rdev);
144457e252bfSMichael Neumann 	if (ret)
144557e252bfSMichael Neumann 		return ret;
144657e252bfSMichael Neumann 
14472c5cc6b9SFrançois Tigeot 	INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
14482c5cc6b9SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
144957e252bfSMichael Neumann 	radeon_dpm_init(rdev);
145057e252bfSMichael Neumann 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
1451c6f73aabSFrançois Tigeot 	if (radeon_dpm == 1)
145257e252bfSMichael Neumann 		radeon_dpm_print_power_states(rdev);
145357e252bfSMichael Neumann 	radeon_dpm_setup_asic(rdev);
145457e252bfSMichael Neumann 	ret = radeon_dpm_enable(rdev);
14552c5cc6b9SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1456c6f73aabSFrançois Tigeot 	if (ret)
1457c6f73aabSFrançois Tigeot 		goto dpm_failed;
1458c6f73aabSFrançois Tigeot 	rdev->pm.dpm_enabled = true;
1459c6f73aabSFrançois Tigeot 
1460c6f73aabSFrançois Tigeot #ifdef TODO_DEVICE_FILE
1461c6f73aabSFrançois Tigeot 	if (radeon_debugfs_pm_init(rdev)) {
1462c6f73aabSFrançois Tigeot 		DRM_ERROR("Failed to register debugfs file for dpm!\n");
1463c6f73aabSFrançois Tigeot 	}
1464c6f73aabSFrançois Tigeot #endif
1465c6f73aabSFrançois Tigeot 
1466c6f73aabSFrançois Tigeot 	DRM_INFO("radeon: dpm initialized\n");
1467c6f73aabSFrançois Tigeot 
1468c6f73aabSFrançois Tigeot 	return 0;
1469c6f73aabSFrançois Tigeot 
1470c6f73aabSFrançois Tigeot dpm_failed:
147157e252bfSMichael Neumann 	rdev->pm.dpm_enabled = false;
147257e252bfSMichael Neumann 	if ((rdev->family >= CHIP_BARTS) &&
14734cd92098Szrj 	    (rdev->family <= CHIP_CAYMAN) &&
147457e252bfSMichael Neumann 	    rdev->mc_fw) {
147557e252bfSMichael Neumann 		if (rdev->pm.default_vddc)
147657e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
147757e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDC);
147857e252bfSMichael Neumann 		if (rdev->pm.default_vddci)
147957e252bfSMichael Neumann 			radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
148057e252bfSMichael Neumann 						SET_VOLTAGE_TYPE_ASIC_VDDCI);
148157e252bfSMichael Neumann 		if (rdev->pm.default_sclk)
148257e252bfSMichael Neumann 			radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
148357e252bfSMichael Neumann 		if (rdev->pm.default_mclk)
148457e252bfSMichael Neumann 			radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
148557e252bfSMichael Neumann 	}
148657e252bfSMichael Neumann 	DRM_ERROR("radeon: dpm initialization failed\n");
148757e252bfSMichael Neumann 	return ret;
148857e252bfSMichael Neumann }
148957e252bfSMichael Neumann 
14907dcf36dcSFrançois Tigeot struct radeon_dpm_quirk {
14917dcf36dcSFrançois Tigeot 	u32 chip_vendor;
14927dcf36dcSFrançois Tigeot 	u32 chip_device;
14937dcf36dcSFrançois Tigeot 	u32 subsys_vendor;
14947dcf36dcSFrançois Tigeot 	u32 subsys_device;
14957dcf36dcSFrançois Tigeot };
14967dcf36dcSFrançois Tigeot 
14977dcf36dcSFrançois Tigeot /* cards with dpm stability problems */
14987dcf36dcSFrançois Tigeot static struct radeon_dpm_quirk radeon_dpm_quirk_list[] = {
14997dcf36dcSFrançois Tigeot 	/* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */
15007dcf36dcSFrançois Tigeot 	{ PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 },
15017dcf36dcSFrançois Tigeot 	/* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */
15027dcf36dcSFrançois Tigeot 	{ PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 },
15037dcf36dcSFrançois Tigeot 	{ 0, 0, 0, 0 },
15047dcf36dcSFrançois Tigeot };
15057dcf36dcSFrançois Tigeot 
radeon_pm_init(struct radeon_device * rdev)150657e252bfSMichael Neumann int radeon_pm_init(struct radeon_device *rdev)
150757e252bfSMichael Neumann {
15087dcf36dcSFrançois Tigeot 	struct radeon_dpm_quirk *p = radeon_dpm_quirk_list;
15097dcf36dcSFrançois Tigeot 	bool disable_dpm = false;
15107dcf36dcSFrançois Tigeot 
15117dcf36dcSFrançois Tigeot 	/* Apply dpm quirks */
15127dcf36dcSFrançois Tigeot 	while (p && p->chip_device != 0) {
15137dcf36dcSFrançois Tigeot 		if (rdev->pdev->vendor == p->chip_vendor &&
15147dcf36dcSFrançois Tigeot 		    rdev->pdev->device == p->chip_device &&
15157dcf36dcSFrançois Tigeot 		    rdev->pdev->subsystem_vendor == p->subsys_vendor &&
15167dcf36dcSFrançois Tigeot 		    rdev->pdev->subsystem_device == p->subsys_device) {
15177dcf36dcSFrançois Tigeot 			disable_dpm = true;
15187dcf36dcSFrançois Tigeot 			break;
15197dcf36dcSFrançois Tigeot 		}
15207dcf36dcSFrançois Tigeot 		++p;
15217dcf36dcSFrançois Tigeot 	}
15227dcf36dcSFrançois Tigeot 
152357e252bfSMichael Neumann 	/* enable dpm on rv6xx+ */
152457e252bfSMichael Neumann 	switch (rdev->family) {
152557e252bfSMichael Neumann 	case CHIP_RV610:
152657e252bfSMichael Neumann 	case CHIP_RV630:
152757e252bfSMichael Neumann 	case CHIP_RV620:
152857e252bfSMichael Neumann 	case CHIP_RV635:
152957e252bfSMichael Neumann 	case CHIP_RV670:
153057e252bfSMichael Neumann 	case CHIP_RS780:
153157e252bfSMichael Neumann 	case CHIP_RS880:
153257e252bfSMichael Neumann 	case CHIP_RV770:
1533c6f73aabSFrançois Tigeot 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
1534c6f73aabSFrançois Tigeot 		if (!rdev->rlc_fw)
1535c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1536c6f73aabSFrançois Tigeot 		else if ((rdev->family >= CHIP_RV770) &&
1537c6f73aabSFrançois Tigeot 			 (!(rdev->flags & RADEON_IS_IGP)) &&
1538c6f73aabSFrançois Tigeot 			 (!rdev->smc_fw))
1539c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1540c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 1)
1541c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
1542c6f73aabSFrançois Tigeot 		else
1543c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1544c6f73aabSFrançois Tigeot 		break;
154557e252bfSMichael Neumann 	case CHIP_RV730:
154657e252bfSMichael Neumann 	case CHIP_RV710:
154757e252bfSMichael Neumann 	case CHIP_RV740:
154857e252bfSMichael Neumann 	case CHIP_CEDAR:
154957e252bfSMichael Neumann 	case CHIP_REDWOOD:
155057e252bfSMichael Neumann 	case CHIP_JUNIPER:
155157e252bfSMichael Neumann 	case CHIP_CYPRESS:
155257e252bfSMichael Neumann 	case CHIP_HEMLOCK:
155357e252bfSMichael Neumann 	case CHIP_PALM:
155457e252bfSMichael Neumann 	case CHIP_SUMO:
155557e252bfSMichael Neumann 	case CHIP_SUMO2:
155657e252bfSMichael Neumann 	case CHIP_BARTS:
155757e252bfSMichael Neumann 	case CHIP_TURKS:
155857e252bfSMichael Neumann 	case CHIP_CAICOS:
155957e252bfSMichael Neumann 	case CHIP_CAYMAN:
156057e252bfSMichael Neumann 	case CHIP_ARUBA:
156157e252bfSMichael Neumann 	case CHIP_TAHITI:
156257e252bfSMichael Neumann 	case CHIP_PITCAIRN:
156357e252bfSMichael Neumann 	case CHIP_VERDE:
156457e252bfSMichael Neumann 	case CHIP_OLAND:
156557e252bfSMichael Neumann 	case CHIP_HAINAN:
15664cd92098Szrj 	case CHIP_BONAIRE:
15674cd92098Szrj 	case CHIP_KABINI:
15684cd92098Szrj 	case CHIP_KAVERI:
1569c6f73aabSFrançois Tigeot 	case CHIP_HAWAII:
1570c6f73aabSFrançois Tigeot 	case CHIP_MULLINS:
157157e252bfSMichael Neumann 		/* DPM requires the RLC, RV770+ dGPU requires SMC */
157257e252bfSMichael Neumann 		if (!rdev->rlc_fw)
157357e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
157457e252bfSMichael Neumann 		else if ((rdev->family >= CHIP_RV770) &&
157557e252bfSMichael Neumann 			 (!(rdev->flags & RADEON_IS_IGP)) &&
157657e252bfSMichael Neumann 			 (!rdev->smc_fw))
157757e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
15787dcf36dcSFrançois Tigeot 		else if (disable_dpm && (radeon_dpm == -1))
15797dcf36dcSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1580c6f73aabSFrançois Tigeot 		else if (radeon_dpm == 0)
158157e252bfSMichael Neumann 			rdev->pm.pm_method = PM_METHOD_PROFILE;
1582c6f73aabSFrançois Tigeot 		else
1583c6f73aabSFrançois Tigeot 			rdev->pm.pm_method = PM_METHOD_DPM;
158457e252bfSMichael Neumann 		break;
158557e252bfSMichael Neumann 	default:
158657e252bfSMichael Neumann 		/* default to profile method */
158757e252bfSMichael Neumann 		rdev->pm.pm_method = PM_METHOD_PROFILE;
158857e252bfSMichael Neumann 		break;
158957e252bfSMichael Neumann 	}
159057e252bfSMichael Neumann 
159157e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
159257e252bfSMichael Neumann 		return radeon_pm_init_dpm(rdev);
159357e252bfSMichael Neumann 	else
159457e252bfSMichael Neumann 		return radeon_pm_init_old(rdev);
159557e252bfSMichael Neumann }
159657e252bfSMichael Neumann 
radeon_pm_late_init(struct radeon_device * rdev)1597c6f73aabSFrançois Tigeot int radeon_pm_late_init(struct radeon_device *rdev)
1598c6f73aabSFrançois Tigeot {
1599c6f73aabSFrançois Tigeot 	int ret = 0;
1600c6f73aabSFrançois Tigeot 
1601c6f73aabSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_DPM) {
1602c59a5c48SFrançois Tigeot 		if (rdev->pm.dpm_enabled) {
1603c59a5c48SFrançois Tigeot 			if (!rdev->pm.sysfs_initialized) {
1604c59a5c48SFrançois Tigeot #if 0
1605c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
1606c59a5c48SFrançois Tigeot 				if (ret)
1607c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for dpm state\n");
1608c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
1609c59a5c48SFrançois Tigeot 				if (ret)
1610c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for dpm state\n");
1611c59a5c48SFrançois Tigeot 				/* XXX: these are noops for dpm but are here for backwards compat */
1612c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1613c59a5c48SFrançois Tigeot 				if (ret)
1614c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for power profile\n");
1615c59a5c48SFrançois Tigeot 				ret = device_create_file(rdev->dev, &dev_attr_power_method);
1616c59a5c48SFrançois Tigeot 				if (ret)
1617c59a5c48SFrançois Tigeot 					DRM_ERROR("failed to create device file for power method\n");
1618c59a5c48SFrançois Tigeot #endif
1619c59a5c48SFrançois Tigeot 				rdev->pm.sysfs_initialized = true;
1620c59a5c48SFrançois Tigeot 			}
1621c59a5c48SFrançois Tigeot 
16221cfef1a5SFrançois Tigeot 			mutex_lock(&rdev->pm.mutex);
1623c6f73aabSFrançois Tigeot 			ret = radeon_dpm_late_enable(rdev);
16241cfef1a5SFrançois Tigeot 			mutex_unlock(&rdev->pm.mutex);
1625c59a5c48SFrançois Tigeot 			if (ret) {
1626c59a5c48SFrançois Tigeot 				rdev->pm.dpm_enabled = false;
1627c59a5c48SFrançois Tigeot 				DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n");
1628c59a5c48SFrançois Tigeot 			} else {
1629c59a5c48SFrançois Tigeot 				/* set the dpm state for PX since there won't be
1630c59a5c48SFrançois Tigeot 				 * a modeset to call this.
1631c59a5c48SFrançois Tigeot 				 */
1632c59a5c48SFrançois Tigeot 				radeon_pm_compute_clocks(rdev);
1633c59a5c48SFrançois Tigeot 			}
1634c59a5c48SFrançois Tigeot 		}
1635c59a5c48SFrançois Tigeot 	} else {
1636c59a5c48SFrançois Tigeot 		if ((rdev->pm.num_power_states > 1) &&
1637c59a5c48SFrançois Tigeot 		    (!rdev->pm.sysfs_initialized)) {
1638c59a5c48SFrançois Tigeot 			/* where's the best place to put these? */
1639c59a5c48SFrançois Tigeot #if 0
1640c59a5c48SFrançois Tigeot 			ret = device_create_file(rdev->dev, &dev_attr_power_profile);
1641c59a5c48SFrançois Tigeot 			if (ret)
1642c59a5c48SFrançois Tigeot 				DRM_ERROR("failed to create device file for power profile\n");
1643c59a5c48SFrançois Tigeot 			ret = device_create_file(rdev->dev, &dev_attr_power_method);
1644c59a5c48SFrançois Tigeot 			if (ret)
1645c59a5c48SFrançois Tigeot 				DRM_ERROR("failed to create device file for power method\n");
1646c59a5c48SFrançois Tigeot 			if (!ret)
1647c59a5c48SFrançois Tigeot 				rdev->pm.sysfs_initialized = true;
1648c59a5c48SFrançois Tigeot #endif
1649c59a5c48SFrançois Tigeot 		}
1650c6f73aabSFrançois Tigeot 	}
1651c6f73aabSFrançois Tigeot 	return ret;
1652c6f73aabSFrançois Tigeot }
1653c6f73aabSFrançois Tigeot 
radeon_pm_fini_old(struct radeon_device * rdev)165457e252bfSMichael Neumann static void radeon_pm_fini_old(struct radeon_device *rdev)
1655926deccbSFrançois Tigeot {
1656926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states > 1) {
16571cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
1658926deccbSFrançois Tigeot 		if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1659926deccbSFrançois Tigeot 			rdev->pm.profile = PM_PROFILE_DEFAULT;
1660926deccbSFrançois Tigeot 			radeon_pm_update_profile(rdev);
1661926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1662926deccbSFrançois Tigeot 		} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1663926deccbSFrançois Tigeot 			/* reset default clocks */
1664926deccbSFrançois Tigeot 			rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
1665926deccbSFrançois Tigeot 			rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1666926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1667926deccbSFrançois Tigeot 		}
16681cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
1669926deccbSFrançois Tigeot 
1670926deccbSFrançois Tigeot #ifdef DUMBBELL_WIP
1671ee479021SImre Vadász 		cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
1672ee479021SImre Vadász 
1673926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_profile);
1674926deccbSFrançois Tigeot 		device_remove_file(rdev->dev, &dev_attr_power_method);
1675926deccbSFrançois Tigeot #endif /* DUMBBELL_WIP */
1676926deccbSFrançois Tigeot 	}
1677926deccbSFrançois Tigeot 
1678926deccbSFrançois Tigeot 	if (rdev->pm.power_state) {
1679926deccbSFrançois Tigeot 		int i;
1680926deccbSFrançois Tigeot 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
1681c4ef309bSzrj 			kfree(rdev->pm.power_state[i].clock_info);
1682926deccbSFrançois Tigeot 		}
1683c4ef309bSzrj 		kfree(rdev->pm.power_state);
1684926deccbSFrançois Tigeot 		rdev->pm.power_state = NULL;
1685926deccbSFrançois Tigeot 		rdev->pm.num_power_states = 0;
1686926deccbSFrançois Tigeot 	}
1687926deccbSFrançois Tigeot 
1688926deccbSFrançois Tigeot 	radeon_hwmon_fini(rdev);
1689926deccbSFrançois Tigeot }
1690926deccbSFrançois Tigeot 
radeon_pm_fini_dpm(struct radeon_device * rdev)169157e252bfSMichael Neumann static void radeon_pm_fini_dpm(struct radeon_device *rdev)
169257e252bfSMichael Neumann {
169357e252bfSMichael Neumann 	if (rdev->pm.num_power_states > 1) {
16941cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
169557e252bfSMichael Neumann 		radeon_dpm_disable(rdev);
16961cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
169757e252bfSMichael Neumann 
169857e252bfSMichael Neumann #ifdef TODO_DEVICE_FILE
169957e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
170057e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
170157e252bfSMichael Neumann 		/* XXX backwards compat */
170257e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_profile);
170357e252bfSMichael Neumann 		device_remove_file(rdev->dev, &dev_attr_power_method);
170457e252bfSMichael Neumann #endif
170557e252bfSMichael Neumann 	}
170657e252bfSMichael Neumann 	radeon_dpm_fini(rdev);
170757e252bfSMichael Neumann 
17085d6a9071Szrj 	/* prevents leaking 440 bytes on OLAND */
17095d6a9071Szrj 	if (rdev->pm.power_state) {
17105d6a9071Szrj 		int i;
17115d6a9071Szrj 		for (i = 0; i < rdev->pm.num_power_states; ++i) {
17125d6a9071Szrj 			kfree(rdev->pm.power_state[i].clock_info);
17135d6a9071Szrj 		}
171457e252bfSMichael Neumann 		kfree(rdev->pm.power_state);
17155d6a9071Szrj 		rdev->pm.power_state = NULL;
17165d6a9071Szrj 		rdev->pm.num_power_states = 0;
17175d6a9071Szrj 	}
171857e252bfSMichael Neumann 
171957e252bfSMichael Neumann 	radeon_hwmon_fini(rdev);
172057e252bfSMichael Neumann }
172157e252bfSMichael Neumann 
radeon_pm_fini(struct radeon_device * rdev)172257e252bfSMichael Neumann void radeon_pm_fini(struct radeon_device *rdev)
172357e252bfSMichael Neumann {
172457e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
172557e252bfSMichael Neumann 		radeon_pm_fini_dpm(rdev);
172657e252bfSMichael Neumann 	else
172757e252bfSMichael Neumann 		radeon_pm_fini_old(rdev);
172857e252bfSMichael Neumann }
172957e252bfSMichael Neumann 
radeon_pm_compute_clocks_old(struct radeon_device * rdev)173057e252bfSMichael Neumann static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
1731926deccbSFrançois Tigeot {
1732926deccbSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1733926deccbSFrançois Tigeot 	struct drm_crtc *crtc;
1734926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc;
1735926deccbSFrançois Tigeot 
1736926deccbSFrançois Tigeot 	if (rdev->pm.num_power_states < 2)
1737926deccbSFrançois Tigeot 		return;
1738926deccbSFrançois Tigeot 
17391cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1740926deccbSFrançois Tigeot 
1741926deccbSFrançois Tigeot 	rdev->pm.active_crtcs = 0;
1742926deccbSFrançois Tigeot 	rdev->pm.active_crtc_count = 0;
1743c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
1744926deccbSFrançois Tigeot 		list_for_each_entry(crtc,
1745926deccbSFrançois Tigeot 				    &ddev->mode_config.crtc_list, head) {
1746926deccbSFrançois Tigeot 			radeon_crtc = to_radeon_crtc(crtc);
1747926deccbSFrançois Tigeot 			if (radeon_crtc->enabled) {
1748926deccbSFrançois Tigeot 				rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
1749926deccbSFrançois Tigeot 				rdev->pm.active_crtc_count++;
1750926deccbSFrançois Tigeot 			}
1751926deccbSFrançois Tigeot 		}
1752c6f73aabSFrançois Tigeot 	}
1753926deccbSFrançois Tigeot 
1754926deccbSFrançois Tigeot 	if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
1755926deccbSFrançois Tigeot 		radeon_pm_update_profile(rdev);
1756926deccbSFrançois Tigeot 		radeon_pm_set_clocks(rdev);
1757926deccbSFrançois Tigeot 	} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
1758926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_state != DYNPM_STATE_DISABLED) {
1759926deccbSFrançois Tigeot 			if (rdev->pm.active_crtc_count > 1) {
1760926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1761ee479021SImre Vadász #ifdef DUMBBELL_WIP
1762926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1763ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1764926deccbSFrançois Tigeot 
1765926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_PAUSED;
1766926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
1767926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1768926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1769926deccbSFrançois Tigeot 
1770926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management deactivated\n");
1771926deccbSFrançois Tigeot 				}
1772926deccbSFrançois Tigeot 			} else if (rdev->pm.active_crtc_count == 1) {
1773926deccbSFrançois Tigeot 				/* TODO: Increase clocks if needed for current mode */
1774926deccbSFrançois Tigeot 
1775926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state == DYNPM_STATE_MINIMUM) {
1776926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1777926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_UPCLOCK;
1778926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1779926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1780926deccbSFrançois Tigeot 
1781ee479021SImre Vadász #ifdef DUMBBELL_WIP
1782926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1783926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1784ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1785926deccbSFrançois Tigeot 				} else if (rdev->pm.dynpm_state == DYNPM_STATE_PAUSED) {
1786926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
1787ee479021SImre Vadász #ifdef DUMBBELL_WIP
1788926deccbSFrançois Tigeot 					schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1789926deccbSFrançois Tigeot 							      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1790ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1791926deccbSFrançois Tigeot 					DRM_DEBUG_DRIVER("radeon: dynamic power management activated\n");
1792926deccbSFrançois Tigeot 				}
1793926deccbSFrançois Tigeot 			} else { /* count == 0 */
1794926deccbSFrançois Tigeot 				if (rdev->pm.dynpm_state != DYNPM_STATE_MINIMUM) {
1795ee479021SImre Vadász #ifdef DUMBBELL_WIP
1796926deccbSFrançois Tigeot 					cancel_delayed_work(&rdev->pm.dynpm_idle_work);
1797ee479021SImre Vadász #endif /* DUMBBELL_WIP */
1798926deccbSFrançois Tigeot 
1799926deccbSFrançois Tigeot 					rdev->pm.dynpm_state = DYNPM_STATE_MINIMUM;
1800926deccbSFrançois Tigeot 					rdev->pm.dynpm_planned_action = DYNPM_ACTION_MINIMUM;
1801926deccbSFrançois Tigeot 					radeon_pm_get_dynpm_state(rdev);
1802926deccbSFrançois Tigeot 					radeon_pm_set_clocks(rdev);
1803926deccbSFrançois Tigeot 				}
1804926deccbSFrançois Tigeot 			}
1805926deccbSFrançois Tigeot 		}
1806926deccbSFrançois Tigeot 	}
1807926deccbSFrançois Tigeot 
18081cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1809926deccbSFrançois Tigeot }
1810926deccbSFrançois Tigeot 
radeon_pm_compute_clocks_dpm(struct radeon_device * rdev)181157e252bfSMichael Neumann static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
181257e252bfSMichael Neumann {
181357e252bfSMichael Neumann 	struct drm_device *ddev = rdev->ddev;
181457e252bfSMichael Neumann 	struct drm_crtc *crtc;
181557e252bfSMichael Neumann 	struct radeon_crtc *radeon_crtc;
181657e252bfSMichael Neumann 
1817c6f73aabSFrançois Tigeot 	if (!rdev->pm.dpm_enabled)
1818c6f73aabSFrançois Tigeot 		return;
1819c6f73aabSFrançois Tigeot 
18201cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
182157e252bfSMichael Neumann 
182257e252bfSMichael Neumann 	/* update active crtc counts */
182357e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtcs = 0;
182457e252bfSMichael Neumann 	rdev->pm.dpm.new_active_crtc_count = 0;
1825c6f73aabSFrançois Tigeot 	if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
182657e252bfSMichael Neumann 		list_for_each_entry(crtc,
182757e252bfSMichael Neumann 				    &ddev->mode_config.crtc_list, head) {
182857e252bfSMichael Neumann 			radeon_crtc = to_radeon_crtc(crtc);
182957e252bfSMichael Neumann 			if (crtc->enabled) {
183057e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
183157e252bfSMichael Neumann 				rdev->pm.dpm.new_active_crtc_count++;
183257e252bfSMichael Neumann 			}
183357e252bfSMichael Neumann 		}
1834c6f73aabSFrançois Tigeot 	}
183557e252bfSMichael Neumann 
183657e252bfSMichael Neumann 	/* update battery/ac status */
183757e252bfSMichael Neumann 	if (power_profile_get_state() == POWER_PROFILE_PERFORMANCE)
183857e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = true;
183957e252bfSMichael Neumann 	else
184057e252bfSMichael Neumann 		rdev->pm.dpm.ac_power = false;
184157e252bfSMichael Neumann 
184257e252bfSMichael Neumann 	radeon_dpm_change_power_state_locked(rdev);
184357e252bfSMichael Neumann 
18441cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1845c6f73aabSFrançois Tigeot 
184657e252bfSMichael Neumann }
184757e252bfSMichael Neumann 
radeon_pm_compute_clocks(struct radeon_device * rdev)184857e252bfSMichael Neumann void radeon_pm_compute_clocks(struct radeon_device *rdev)
184957e252bfSMichael Neumann {
185057e252bfSMichael Neumann 	if (rdev->pm.pm_method == PM_METHOD_DPM)
185157e252bfSMichael Neumann 		radeon_pm_compute_clocks_dpm(rdev);
185257e252bfSMichael Neumann 	else
185357e252bfSMichael Neumann 		radeon_pm_compute_clocks_old(rdev);
185457e252bfSMichael Neumann }
185557e252bfSMichael Neumann 
radeon_pm_in_vbl(struct radeon_device * rdev)1856926deccbSFrançois Tigeot static bool radeon_pm_in_vbl(struct radeon_device *rdev)
1857926deccbSFrançois Tigeot {
1858926deccbSFrançois Tigeot 	int  crtc, vpos, hpos, vbl_status;
1859926deccbSFrançois Tigeot 	bool in_vbl = true;
1860926deccbSFrançois Tigeot 
1861926deccbSFrançois Tigeot 	/* Iterate over all active crtc's. All crtc's must be in vblank,
1862926deccbSFrançois Tigeot 	 * otherwise return in_vbl == false.
1863926deccbSFrançois Tigeot 	 */
1864926deccbSFrançois Tigeot 	for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
1865926deccbSFrançois Tigeot 		if (rdev->pm.active_crtcs & (1 << crtc)) {
1866c59a5c48SFrançois Tigeot 			vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev,
1867c59a5c48SFrançois Tigeot 								crtc,
1868c59a5c48SFrançois Tigeot 								USE_REAL_VBLANKSTART,
1869352ff8bdSFrançois Tigeot 								&vpos, &hpos, NULL, NULL,
1870352ff8bdSFrançois Tigeot 								&rdev->mode_info.crtcs[crtc]->base.hwmode);
1871926deccbSFrançois Tigeot 			if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
18721b13d190SFrançois Tigeot 			    !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK))
1873926deccbSFrançois Tigeot 				in_vbl = false;
1874926deccbSFrançois Tigeot 		}
1875926deccbSFrançois Tigeot 	}
1876926deccbSFrançois Tigeot 
1877926deccbSFrançois Tigeot 	return in_vbl;
1878926deccbSFrançois Tigeot }
1879926deccbSFrançois Tigeot 
radeon_pm_debug_check_in_vbl(struct radeon_device * rdev,bool finish)1880926deccbSFrançois Tigeot static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
1881926deccbSFrançois Tigeot {
1882926deccbSFrançois Tigeot 	u32 stat_crtc = 0;
1883926deccbSFrançois Tigeot 	bool in_vbl = radeon_pm_in_vbl(rdev);
1884926deccbSFrançois Tigeot 
1885926deccbSFrançois Tigeot 	if (in_vbl == false)
1886926deccbSFrançois Tigeot 		DRM_DEBUG_DRIVER("not in vbl for pm change %08x at %s\n", stat_crtc,
1887926deccbSFrançois Tigeot 			 finish ? "exit" : "entry");
1888926deccbSFrançois Tigeot 	return in_vbl;
1889926deccbSFrançois Tigeot }
1890926deccbSFrançois Tigeot 
radeon_dynpm_idle_work_handler(struct work_struct * work)1891926deccbSFrançois Tigeot static void radeon_dynpm_idle_work_handler(struct work_struct *work)
1892926deccbSFrançois Tigeot {
1893926deccbSFrançois Tigeot 	struct radeon_device *rdev;
1894926deccbSFrançois Tigeot 	int resched;
1895926deccbSFrançois Tigeot 	rdev = container_of(work, struct radeon_device,
1896926deccbSFrançois Tigeot 				pm.dynpm_idle_work.work);
1897926deccbSFrançois Tigeot 
1898926deccbSFrançois Tigeot 	resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
18991cfef1a5SFrançois Tigeot 	mutex_lock(&rdev->pm.mutex);
1900926deccbSFrançois Tigeot 	if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) {
1901926deccbSFrançois Tigeot 		int not_processed = 0;
1902926deccbSFrançois Tigeot 		int i;
1903926deccbSFrançois Tigeot 
1904926deccbSFrançois Tigeot 		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
1905926deccbSFrançois Tigeot 			struct radeon_ring *ring = &rdev->ring[i];
1906926deccbSFrançois Tigeot 
1907926deccbSFrançois Tigeot 			if (ring->ready) {
1908926deccbSFrançois Tigeot 				not_processed += radeon_fence_count_emitted(rdev, i);
1909926deccbSFrançois Tigeot 				if (not_processed >= 3)
1910926deccbSFrançois Tigeot 					break;
1911926deccbSFrançois Tigeot 			}
1912926deccbSFrançois Tigeot 		}
1913926deccbSFrançois Tigeot 
1914926deccbSFrançois Tigeot 		if (not_processed >= 3) { /* should upclock */
1915926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_DOWNCLOCK) {
1916926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1917926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1918926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_upclock) {
1919926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1920926deccbSFrançois Tigeot 					DYNPM_ACTION_UPCLOCK;
1921926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1922926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1923926deccbSFrançois Tigeot 			}
1924926deccbSFrançois Tigeot 		} else if (not_processed == 0) { /* should downclock */
1925926deccbSFrançois Tigeot 			if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_UPCLOCK) {
1926926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
1927926deccbSFrançois Tigeot 			} else if (rdev->pm.dynpm_planned_action == DYNPM_ACTION_NONE &&
1928926deccbSFrançois Tigeot 				   rdev->pm.dynpm_can_downclock) {
1929926deccbSFrançois Tigeot 				rdev->pm.dynpm_planned_action =
1930926deccbSFrançois Tigeot 					DYNPM_ACTION_DOWNCLOCK;
1931926deccbSFrançois Tigeot 				rdev->pm.dynpm_action_timeout = jiffies +
1932926deccbSFrançois Tigeot 				msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
1933926deccbSFrançois Tigeot 			}
1934926deccbSFrançois Tigeot 		}
1935926deccbSFrançois Tigeot 
1936926deccbSFrançois Tigeot 		/* Note, radeon_pm_set_clocks is called with static_switch set
1937926deccbSFrançois Tigeot 		 * to false since we want to wait for vbl to avoid flicker.
1938926deccbSFrançois Tigeot 		 */
1939926deccbSFrançois Tigeot 		if (rdev->pm.dynpm_planned_action != DYNPM_ACTION_NONE &&
1940926deccbSFrançois Tigeot 		    jiffies > rdev->pm.dynpm_action_timeout) {
1941926deccbSFrançois Tigeot 			radeon_pm_get_dynpm_state(rdev);
1942926deccbSFrançois Tigeot 			radeon_pm_set_clocks(rdev);
1943926deccbSFrançois Tigeot 		}
1944926deccbSFrançois Tigeot 
1945926deccbSFrançois Tigeot 		schedule_delayed_work(&rdev->pm.dynpm_idle_work,
1946926deccbSFrançois Tigeot 				      msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
1947926deccbSFrançois Tigeot 	}
19481cfef1a5SFrançois Tigeot 	mutex_unlock(&rdev->pm.mutex);
1949926deccbSFrançois Tigeot 	ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
1950926deccbSFrançois Tigeot }
1951926deccbSFrançois Tigeot 
1952926deccbSFrançois Tigeot /*
1953926deccbSFrançois Tigeot  * Debugfs info
1954926deccbSFrançois Tigeot  */
1955926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
1956926deccbSFrançois Tigeot 
radeon_debugfs_pm_info(struct seq_file * m,void * data)1957926deccbSFrançois Tigeot static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
1958926deccbSFrançois Tigeot {
1959926deccbSFrançois Tigeot 	struct drm_info_node *node = (struct drm_info_node *) m->private;
1960926deccbSFrançois Tigeot 	struct drm_device *dev = node->minor->dev;
1961926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
1962c6f73aabSFrançois Tigeot 	struct drm_device *ddev = rdev->ddev;
1963926deccbSFrançois Tigeot 
1964c6f73aabSFrançois Tigeot 	if  ((rdev->flags & RADEON_IS_PX) &&
1965c6f73aabSFrançois Tigeot 	     (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
1966c6f73aabSFrançois Tigeot 		seq_printf(m, "PX asic powered off\n");
1967c6f73aabSFrançois Tigeot 	} else if (rdev->pm.dpm_enabled) {
19681cfef1a5SFrançois Tigeot 		mutex_lock(&rdev->pm.mutex);
196957e252bfSMichael Neumann 		if (rdev->asic->dpm.debugfs_print_current_performance_level)
197057e252bfSMichael Neumann 			radeon_dpm_debugfs_print_current_performance_level(rdev, m);
197157e252bfSMichael Neumann 		else
197257e252bfSMichael Neumann 			seq_printf(m, "Debugfs support not implemented for this asic\n");
19731cfef1a5SFrançois Tigeot 		mutex_unlock(&rdev->pm.mutex);
197457e252bfSMichael Neumann 	} else {
1975926deccbSFrançois Tigeot 		seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
1976f43cf1b1SMichael Neumann 		/* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
1977f43cf1b1SMichael Neumann 		if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
1978f43cf1b1SMichael Neumann 			seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
1979f43cf1b1SMichael Neumann 		else
1980926deccbSFrançois Tigeot 			seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
1981926deccbSFrançois Tigeot 		seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
1982926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_memory_clock)
1983926deccbSFrançois Tigeot 			seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
1984926deccbSFrançois Tigeot 		if (rdev->pm.current_vddc)
1985926deccbSFrançois Tigeot 			seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
1986926deccbSFrançois Tigeot 		if (rdev->asic->pm.get_pcie_lanes)
1987926deccbSFrançois Tigeot 			seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
198857e252bfSMichael Neumann 	}
1989926deccbSFrançois Tigeot 
1990926deccbSFrançois Tigeot 	return 0;
1991926deccbSFrançois Tigeot }
1992926deccbSFrançois Tigeot 
1993926deccbSFrançois Tigeot static struct drm_info_list radeon_pm_info_list[] = {
1994926deccbSFrançois Tigeot 	{"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL},
1995926deccbSFrançois Tigeot };
1996926deccbSFrançois Tigeot #endif
1997926deccbSFrançois Tigeot 
radeon_debugfs_pm_init(struct radeon_device * rdev)1998926deccbSFrançois Tigeot static int radeon_debugfs_pm_init(struct radeon_device *rdev)
1999926deccbSFrançois Tigeot {
2000926deccbSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
2001926deccbSFrançois Tigeot 	return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
2002926deccbSFrançois Tigeot #else
2003926deccbSFrançois Tigeot 	return 0;
2004926deccbSFrançois Tigeot #endif
2005926deccbSFrançois Tigeot }
2006