mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/msm/a6xx: Add devfreq support for a6xx
Implement routines to estimate GPU busy time and fetching the current frequency for the polling interval. This is required by the devfreq framework which recommends a frequency change if needed. The driver code then tries to set this new frequency on the GPU by sending an Out Of Band(OOB) request to the GMU. Signed-off-by: Sharat Masetty <smasetty@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
		
							parent
							
								
									de0a3d094d
								
							
						
					
					
						commit
						a2c3c0a54d
					
				
					 4 changed files with 67 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -61,8 +61,10 @@ static bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu)
 | 
			
		|||
		A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int a6xx_gmu_set_freq(struct a6xx_gmu *gmu, int index)
 | 
			
		||||
static void __a6xx_gmu_set_freq(struct a6xx_gmu *gmu, int index)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	gmu_write(gmu, REG_A6XX_GMU_DCVS_ACK_OPTION, 0);
 | 
			
		||||
 | 
			
		||||
	gmu_write(gmu, REG_A6XX_GMU_DCVS_PERF_SETTING,
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +80,37 @@ static int a6xx_gmu_set_freq(struct a6xx_gmu *gmu, int index)
 | 
			
		|||
	a6xx_gmu_set_oob(gmu, GMU_OOB_DCVS_SET);
 | 
			
		||||
	a6xx_gmu_clear_oob(gmu, GMU_OOB_DCVS_SET);
 | 
			
		||||
 | 
			
		||||
	return gmu_read(gmu, REG_A6XX_GMU_DCVS_RETURN);
 | 
			
		||||
	ret = gmu_read(gmu, REG_A6XX_GMU_DCVS_RETURN);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret);
 | 
			
		||||
 | 
			
		||||
	gmu->freq = gmu->gpu_freqs[index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void a6xx_gmu_set_freq(struct msm_gpu *gpu, unsigned long freq)
 | 
			
		||||
{
 | 
			
		||||
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 | 
			
		||||
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
 | 
			
		||||
	struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
 | 
			
		||||
	u32 perf_index = 0;
 | 
			
		||||
 | 
			
		||||
	if (freq == gmu->freq)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for (perf_index = 0; perf_index < gmu->nr_gpu_freqs - 1; perf_index++)
 | 
			
		||||
		if (freq == gmu->gpu_freqs[perf_index])
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
	__a6xx_gmu_set_freq(gmu, perf_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu)
 | 
			
		||||
{
 | 
			
		||||
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 | 
			
		||||
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
 | 
			
		||||
	struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
 | 
			
		||||
 | 
			
		||||
	return  gmu->freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu)
 | 
			
		||||
| 
						 | 
				
			
			@ -637,7 +669,7 @@ int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu)
 | 
			
		|||
		ret = a6xx_hfi_start(gmu, GMU_COLD_BOOT);
 | 
			
		||||
 | 
			
		||||
	/* Set the GPU back to the highest power frequency */
 | 
			
		||||
	a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1);
 | 
			
		||||
	__a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (ret)
 | 
			
		||||
| 
						 | 
				
			
			@ -676,7 +708,7 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
 | 
			
		|||
	ret = a6xx_hfi_start(gmu, status);
 | 
			
		||||
 | 
			
		||||
	/* Set the GPU to the highest power frequency */
 | 
			
		||||
	a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1);
 | 
			
		||||
	__a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	/* Make sure to turn off the boot OOB request on error */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,8 @@ struct a6xx_gmu {
 | 
			
		|||
	unsigned long gmu_freqs[4];
 | 
			
		||||
	u32 cx_arc_votes[4];
 | 
			
		||||
 | 
			
		||||
	unsigned long freq;
 | 
			
		||||
 | 
			
		||||
	struct a6xx_hfi_queue queues[2];
 | 
			
		||||
 | 
			
		||||
	struct tasklet_struct hfi_tasklet;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,8 @@
 | 
			
		|||
#include "a6xx_gpu.h"
 | 
			
		||||
#include "a6xx_gmu.xml.h"
 | 
			
		||||
 | 
			
		||||
#include <linux/devfreq.h>
 | 
			
		||||
 | 
			
		||||
static inline bool _a6xx_check_idle(struct msm_gpu *gpu)
 | 
			
		||||
{
 | 
			
		||||
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 | 
			
		||||
| 
						 | 
				
			
			@ -682,6 +684,8 @@ static int a6xx_pm_resume(struct msm_gpu *gpu)
 | 
			
		|||
 | 
			
		||||
	gpu->needs_hw_init = true;
 | 
			
		||||
 | 
			
		||||
	msm_gpu_resume_devfreq(gpu);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -690,6 +694,8 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu)
 | 
			
		|||
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 | 
			
		||||
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
 | 
			
		||||
 | 
			
		||||
	devfreq_suspend_device(gpu->devfreq.devfreq);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Make sure the GMU is idle before continuing (because some transitions
 | 
			
		||||
	 * may use VBIF
 | 
			
		||||
| 
						 | 
				
			
			@ -753,6 +759,24 @@ static void a6xx_destroy(struct msm_gpu *gpu)
 | 
			
		|||
	kfree(a6xx_gpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long a6xx_gpu_busy(struct msm_gpu *gpu)
 | 
			
		||||
{
 | 
			
		||||
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 | 
			
		||||
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
 | 
			
		||||
	u64 busy_cycles;
 | 
			
		||||
	unsigned long busy_time;
 | 
			
		||||
 | 
			
		||||
	busy_cycles = gmu_read64(&a6xx_gpu->gmu,
 | 
			
		||||
			REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
 | 
			
		||||
			REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);
 | 
			
		||||
 | 
			
		||||
	busy_time = ((busy_cycles - gpu->devfreq.busy_cycles) * 10) / 192;
 | 
			
		||||
 | 
			
		||||
	gpu->devfreq.busy_cycles = busy_cycles;
 | 
			
		||||
 | 
			
		||||
	return busy_time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct adreno_gpu_funcs funcs = {
 | 
			
		||||
	.base = {
 | 
			
		||||
		.get_param = adreno_get_param,
 | 
			
		||||
| 
						 | 
				
			
			@ -768,6 +792,9 @@ static const struct adreno_gpu_funcs funcs = {
 | 
			
		|||
#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 | 
			
		||||
		.show = a6xx_show,
 | 
			
		||||
#endif
 | 
			
		||||
		.gpu_busy = a6xx_gpu_busy,
 | 
			
		||||
		.gpu_get_freq = a6xx_gmu_get_freq,
 | 
			
		||||
		.gpu_set_freq = a6xx_gmu_set_freq,
 | 
			
		||||
	},
 | 
			
		||||
	.get_timestamp = a6xx_get_timestamp,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,5 +56,6 @@ void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state);
 | 
			
		|||
 | 
			
		||||
int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node);
 | 
			
		||||
void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu);
 | 
			
		||||
 | 
			
		||||
void a6xx_gmu_set_freq(struct msm_gpu *gpu, unsigned long freq);
 | 
			
		||||
unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu);
 | 
			
		||||
#endif /* __A6XX_GPU_H__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue