diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_gpu.c')
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.c | 112 |
1 files changed, 83 insertions, 29 deletions
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 232201403439..bd376f9e18a7 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -21,42 +21,90 @@ #include "msm_fence.h" #include <linux/string_helpers.h> +#include <linux/pm_opp.h> +#include <linux/devfreq.h> /* * Power Management: */ -#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING -#include <mach/board.h> -static void bs_init(struct msm_gpu *gpu) +static int msm_devfreq_target(struct device *dev, unsigned long *freq, + u32 flags) { - if (gpu->bus_scale_table) { - gpu->bsc = msm_bus_scale_register_client(gpu->bus_scale_table); - DBG("bus scale client: %08x", gpu->bsc); - } + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + struct dev_pm_opp *opp; + + opp = devfreq_recommended_opp(dev, freq, flags); + + if (IS_ERR(opp)) + return PTR_ERR(opp); + + clk_set_rate(gpu->core_clk, *freq); + dev_pm_opp_put(opp); + + return 0; } -static void bs_fini(struct msm_gpu *gpu) +static int msm_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *status) { - if (gpu->bsc) { - msm_bus_scale_unregister_client(gpu->bsc); - gpu->bsc = 0; - } + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + u64 cycles; + u32 freq = ((u32) status->current_frequency) / 1000000; + ktime_t time; + + status->current_frequency = (unsigned long) clk_get_rate(gpu->core_clk); + gpu->funcs->gpu_busy(gpu, &cycles); + + status->busy_time = ((u32) (cycles - gpu->devfreq.busy_cycles)) / freq; + + gpu->devfreq.busy_cycles = cycles; + + time = ktime_get(); + status->total_time = ktime_us_delta(time, gpu->devfreq.time); + gpu->devfreq.time = time; + + return 0; +} + +static int msm_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + + *freq = (unsigned long) clk_get_rate(gpu->core_clk); + + return 0; } -static void bs_set(struct msm_gpu *gpu, int idx) +static struct devfreq_dev_profile msm_devfreq_profile = { + .polling_ms = 10, + .target = msm_devfreq_target, + .get_dev_status = msm_devfreq_get_dev_status, + .get_cur_freq = msm_devfreq_get_cur_freq, +}; + +static void msm_devfreq_init(struct msm_gpu *gpu) { - if (gpu->bsc) { - DBG("set bus scaling: %d", idx); - msm_bus_scale_client_update_request(gpu->bsc, idx); + /* We need target support to do devfreq */ + if (!gpu->funcs->gpu_busy) + return; + + msm_devfreq_profile.initial_freq = gpu->fast_rate; + + /* + * Don't set the freq_table or max_state and let devfreq build the table + * from OPP + */ + + gpu->devfreq.devfreq = devm_devfreq_add_device(&gpu->pdev->dev, + &msm_devfreq_profile, "simple_ondemand", NULL); + + if (IS_ERR(gpu->devfreq.devfreq)) { + dev_err(&gpu->pdev->dev, "Couldn't initialize GPU devfreq\n"); + gpu->devfreq.devfreq = NULL; } } -#else -static void bs_init(struct msm_gpu *gpu) {} -static void bs_fini(struct msm_gpu *gpu) {} -static void bs_set(struct msm_gpu *gpu, int idx) {} -#endif static int enable_pwrrail(struct msm_gpu *gpu) { @@ -143,8 +191,6 @@ static int enable_axi(struct msm_gpu *gpu) { if (gpu->ebi1_clk) clk_prepare_enable(gpu->ebi1_clk); - if (gpu->bus_freq) - bs_set(gpu, gpu->bus_freq); return 0; } @@ -152,8 +198,6 @@ static int disable_axi(struct msm_gpu *gpu) { if (gpu->ebi1_clk) clk_disable_unprepare(gpu->ebi1_clk); - if (gpu->bus_freq) - bs_set(gpu, 0); return 0; } @@ -175,6 +219,13 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu) if (ret) return ret; + if (gpu->devfreq.devfreq) { + gpu->devfreq.busy_cycles = 0; + gpu->devfreq.time = ktime_get(); + + devfreq_resume_device(gpu->devfreq.devfreq); + } + gpu->needs_hw_init = true; return 0; @@ -186,6 +237,9 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) DBG("%s", gpu->name); + if (gpu->devfreq.devfreq) + devfreq_suspend_device(gpu->devfreq.devfreq); + ret = disable_axi(gpu); if (ret) return ret; @@ -294,6 +348,8 @@ static void recover_worker(struct work_struct *work) msm_rd_dump_submit(priv->hangrd, submit, "offending task: %s (%s)", task->comm, cmd); + + kfree(cmd); } else { msm_rd_dump_submit(priv->hangrd, submit, NULL); } @@ -306,7 +362,7 @@ static void recover_worker(struct work_struct *work) * needs to happen after msm_rd_dump_submit() to ensure that the * bo's referenced by the offending submit are still around. */ - for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { + for (i = 0; i < gpu->nr_rings; i++) { struct msm_ringbuffer *ring = gpu->rb[i]; uint32_t fence = ring->memptrs->fence; @@ -753,7 +809,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->pdev = pdev; platform_set_drvdata(pdev, gpu); - bs_init(gpu); + msm_devfreq_init(gpu); gpu->aspace = msm_gpu_create_address_space(gpu, pdev, config->va_start, config->va_end); @@ -824,8 +880,6 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) WARN_ON(!list_empty(&gpu->active_list)); - bs_fini(gpu); - for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { msm_ringbuffer_destroy(gpu->rb[i]); gpu->rb[i] = NULL; |