summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/adreno
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/adreno')
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c67
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.h17
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_pm.c203
3 files changed, 271 insertions, 16 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index a31360a7ec46..c97f8f41b159 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -254,9 +254,12 @@ static int a3xx_hw_init(struct adreno_gpu *gpu)
static void a3xx_destroy(struct adreno_gpu *gpu)
{
+ struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(gpu);
DBG("%s", adreno_get_name(gpu));
+ a3xx_bs_fini(a3xx_gpu);
+
adreno_gpu_cleanup(gpu);
put_device(&gpu->pdev->dev);
kfree(gpu);
@@ -365,8 +368,8 @@ static void a3xx_show(struct adreno_gpu *gpu, struct seq_file *m)
static const struct adreno_gpu_funcs funcs = {
.get_param = adreno_get_param,
.hw_init = a3xx_hw_init,
- .pm_suspend = msm_gpu_pm_suspend,
- .pm_resume = msm_gpu_pm_resume,
+ .pm_suspend = a3xx_gpu_pm_suspend,
+ .pm_resume = a3xx_gpu_pm_resume,
.recover = adreno_recover,
.last_fence = adreno_last_fence,
.submit = adreno_submit,
@@ -379,14 +382,18 @@ static const struct adreno_gpu_funcs funcs = {
#endif
};
+static const char *clk_names[] = {
+ "src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk",
+};
+
struct msm_gpu *a3xx_gpu_init(struct drm_device *dev, struct adreno_gem *gem,
struct workqueue_struct *wq, int reglog)
{
- struct adreno_gpu *adreno_gpu = NULL;
+ struct a3xx_gpu *a3xx_gpu = NULL;
struct msm_gpu *gpu;
struct platform_device *pdev = a3xx_pdev;
struct adreno_platform_config *config;
- int ret;
+ int i, ret;
if (!pdev) {
dev_err(dev->dev, "no a3xx device\n");
@@ -395,35 +402,63 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev, struct adreno_gem *gem,
config = pdev->dev.platform_data;
- adreno_gpu = kzalloc(sizeof(*adreno_gpu), GFP_KERNEL);
- if (!adreno_gpu) {
+ a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL);
+ if (!a3xx_gpu) {
ret = -ENOMEM;
goto fail;
}
- gpu = &adreno_gpu->base;
+ gpu = &a3xx_gpu->base.base;
get_device(&pdev->dev);
- adreno_gpu->pdev = pdev;
+ a3xx_gpu->base.pdev = pdev;
- gpu->fast_rate = config->fast_rate;
- gpu->slow_rate = config->slow_rate;
- gpu->bus_freq = config->bus_freq;
+ a3xx_gpu->fast_rate = config->fast_rate;
+ a3xx_gpu->slow_rate = config->slow_rate;
+ a3xx_gpu->bus_freq = config->bus_freq;
DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
- gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
+ a3xx_gpu->fast_rate, a3xx_gpu->slow_rate, a3xx_gpu->bus_freq);
+
+ /* Acquire clocks: */
+ BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(a3xx_gpu->grp_clks));
+
+ for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
+ a3xx_gpu->grp_clks[i] = devm_clk_get(&pdev->dev, clk_names[i]);
+ DBG("grp_clks[%s]: %p", clk_names[i], a3xx_gpu->grp_clks[i]);
+ if (IS_ERR(a3xx_gpu->grp_clks[i]))
+ a3xx_gpu->grp_clks[i] = NULL;
+ }
+
+ a3xx_gpu->ebi1_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ DBG("ebi1_clk: %p", a3xx_gpu->ebi1_clk);
+ if (IS_ERR(a3xx_gpu->ebi1_clk))
+ a3xx_gpu->ebi1_clk = NULL;
+
+ /* Acquire regulators: */
+ a3xx_gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd");
+ DBG("gpu_reg: %p", a3xx_gpu->gpu_reg);
+ if (IS_ERR(a3xx_gpu->gpu_reg))
+ a3xx_gpu->gpu_reg = NULL;
+
+ a3xx_gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx");
+ DBG("gpu_cx: %p", a3xx_gpu->gpu_cx);
+ if (IS_ERR(a3xx_gpu->gpu_cx))
+ a3xx_gpu->gpu_cx = NULL;
+
+ a3xx_bs_init(a3xx_gpu, pdev);
adreno_reglog = reglog;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu,
+ ret = adreno_gpu_init(dev, pdev, &a3xx_gpu->base,
&funcs, gem, wq, config->rev);
if (ret)
goto fail;
- return &adreno_gpu->base;
+ return &a3xx_gpu->base.base;
fail:
- if (adreno_gpu)
- a3xx_destroy(adreno_gpu);
+ if (a3xx_gpu)
+ a3xx_destroy(&a3xx_gpu->base);
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
index 00384e9a439c..0070c54ff2d6 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
@@ -21,4 +21,21 @@
#include "adreno_gpu.h"
#include "a3xx.xml.h"
+struct a3xx_gpu {
+ struct adreno_gpu base;
+
+ /* Power Control: */
+ struct regulator *gpu_reg, *gpu_cx;
+ struct clk *ebi1_clk, *grp_clks[5];
+ uint32_t fast_rate, slow_rate, bus_freq;
+ uint32_t bsc;
+};
+#define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base)
+
+int a3xx_gpu_pm_suspend(struct adreno_gpu *gpu);
+int a3xx_gpu_pm_resume(struct adreno_gpu *gpu);
+
+void a3xx_bs_init(struct a3xx_gpu *gpu, struct platform_device *pdev);
+void a3xx_bs_fini(struct a3xx_gpu *gpu);
+
#endif /* __A3XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_pm.c b/drivers/gpu/drm/msm/adreno/a3xx_pm.c
new file mode 100644
index 000000000000..f11ef116bcea
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a3xx_pm.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 Pengutronix
+ * Author: Lucas Stach <l.stach@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <adreno/a3xx_gpu.h>
+
+/*
+ * Power Management:
+ */
+
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/board.h>
+#include <mach/kgsl.h>
+void a3xx_bs_init(struct a3xx_gpu *gpu, struct platform_device *pdev)
+{
+ struct drm_device *dev = gpu->dev;
+ struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdev) {
+ dev_err(dev->dev, "could not find dtv pdata\n");
+ return;
+ }
+
+ if (pdata->bus_scale_table) {
+ gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table);
+ DBG("bus scale client: %08x", gpu->bsc);
+ }
+}
+
+void a3xx_bs_fini(struct a3xx_gpu *gpu)
+{
+ if (gpu->bsc) {
+ msm_bus_scale_unregister_client(gpu->bsc);
+ gpu->bsc = 0;
+ }
+}
+
+static void a3xx_bs_set(struct a3xx_gpu *gpu, int idx)
+{
+ if (gpu->bsc) {
+ DBG("set bus scaling: %d", idx);
+ msm_bus_scale_client_update_request(gpu->bsc, idx);
+ }
+}
+#else
+void a3xx_bs_init(struct a3xx_gpu *gpu, struct platform_device *pdev) {}
+void a3xx_bs_fini(struct a3xx_gpu *gpu) {}
+static void a3xx_bs_set(struct a3xx_gpu *gpu, int idx) {}
+#endif
+
+static int enable_pwrrail(struct a3xx_gpu *gpu)
+{
+ struct drm_device *dev = gpu->base.drm;
+ int ret = 0;
+
+ if (gpu->gpu_reg) {
+ ret = regulator_enable(gpu->gpu_reg);
+ if (ret) {
+ dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (gpu->gpu_cx) {
+ ret = regulator_enable(gpu->gpu_cx);
+ if (ret) {
+ dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int disable_pwrrail(struct a3xx_gpu *gpu)
+{
+ if (gpu->gpu_cx)
+ regulator_disable(gpu->gpu_cx);
+ if (gpu->gpu_reg)
+ regulator_disable(gpu->gpu_reg);
+ return 0;
+}
+
+static int enable_clk(struct a3xx_gpu *gpu)
+{
+ struct clk *rate_clk = NULL;
+ int i;
+
+ /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
+ for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
+ if (gpu->grp_clks[i]) {
+ clk_prepare(gpu->grp_clks[i]);
+ rate_clk = gpu->grp_clks[i];
+ }
+ }
+
+ if (rate_clk && gpu->fast_rate)
+ clk_set_rate(rate_clk, gpu->fast_rate);
+
+ for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
+ if (gpu->grp_clks[i])
+ clk_enable(gpu->grp_clks[i]);
+
+ return 0;
+}
+
+static int disable_clk(struct a3xx_gpu *gpu)
+{
+ struct clk *rate_clk = NULL;
+ int i;
+
+ /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
+ for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
+ if (gpu->grp_clks[i]) {
+ clk_disable(gpu->grp_clks[i]);
+ rate_clk = gpu->grp_clks[i];
+ }
+ }
+
+ if (rate_clk && gpu->slow_rate)
+ clk_set_rate(rate_clk, gpu->slow_rate);
+
+ for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
+ if (gpu->grp_clks[i])
+ clk_unprepare(gpu->grp_clks[i]);
+
+ return 0;
+}
+
+static int enable_axi(struct a3xx_gpu *gpu)
+{
+ if (gpu->ebi1_clk)
+ clk_prepare_enable(gpu->ebi1_clk);
+ if (gpu->bus_freq)
+ a3xx_bs_set(gpu, gpu->bus_freq);
+ return 0;
+}
+
+static int disable_axi(struct a3xx_gpu *gpu)
+{
+ if (gpu->ebi1_clk)
+ clk_disable_unprepare(gpu->ebi1_clk);
+ if (gpu->bus_freq)
+ a3xx_bs_set(gpu, 0);
+ return 0;
+}
+
+int a3xx_gpu_pm_resume(struct adreno_gpu *gpu)
+{
+ struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(gpu);
+ int ret;
+
+ DBG("%s", adreno_get_name(gpu));
+
+ ret = enable_pwrrail(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ ret = enable_clk(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ ret = enable_axi(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int a3xx_gpu_pm_suspend(struct adreno_gpu *gpu)
+{
+ struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(gpu);
+ int ret;
+
+ DBG("%s", adreno_get_name(gpu));
+
+ ret = disable_axi(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ ret = disable_clk(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ ret = disable_pwrrail(a3xx_gpu);
+ if (ret)
+ return ret;
+
+ return 0;
+}