summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2013-11-28 11:21:14 +0100
committerThierry Reding <treding@nvidia.com>2014-01-10 10:15:23 +0100
commit8f54a7728733fe543ff62a10777a5ad74ce8c90b (patch)
treec90bb0187afdda1c7ac98049d445eb730a0a9814
parent71e253b5cd0feb24966ba5884237f5f4849b01a9 (diff)
HACK: drm/tegra: Add gk20a support
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/Makefile2
-rw-r--r--drivers/gpu/drm/tegra/gk20a.c247
2 files changed, 249 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index d43f21bb4596..95f4eb605ca8 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -16,4 +16,6 @@ tegra-drm-y := \
gr2d.o \
gr3d.o
+tegra-drm-y += gk20a.o
+
obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
diff --git a/drivers/gpu/drm/tegra/gk20a.c b/drivers/gpu/drm/tegra/gk20a.c
new file mode 100644
index 000000000000..4b9ce4f6e606
--- /dev/null
+++ b/drivers/gpu/drm/tegra/gk20a.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/tegra-powergate.h>
+
+struct gk20a {
+ struct device *dev;
+ void __iomem *regs;
+ void __iomem *bar1;
+
+ struct reset_control *rst;
+ struct clk *clk;
+ struct clk *clk_pll;
+
+ struct regulator *vdd;
+
+ int irq;
+ int irq_nostall;
+};
+
+static inline unsigned long gk20a_readl(struct gk20a *gpu, unsigned long offset)
+{
+ unsigned long value = readl(gpu->regs + offset);
+ dev_dbg(gpu->dev, "%08lx > %08lx\n", offset, value);
+ return value;
+}
+
+static inline void gk20a_writel(struct gk20a *gpu, unsigned long value,
+ unsigned long offset)
+{
+ dev_dbg(gpu->dev, "%08lx < %08lx\n", offset, value);
+ writel(value, gpu->regs + offset);
+}
+
+static irqreturn_t gk20a_irq(int irq, void *data)
+{
+ struct gk20a *gpu = data;
+
+ dev_dbg(gpu->dev, "> %s(irq=%d, data=%p)\n", __func__, irq, data);
+
+ dev_dbg(gpu->dev, "< %s()\n", __func__);
+ return IRQ_HANDLED;
+}
+
+static int gk20a_power_up(struct gk20a *gpu)
+{
+ int err;
+
+ reset_control_assert(gpu->rst);
+
+ err = clk_enable(gpu->clk);
+ if (err < 0)
+ return err;
+
+ err = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
+ if (err < 0)
+ return err;
+
+ reset_control_deassert(gpu->rst);
+
+ return 0;
+}
+
+static int gk20a_init(struct gk20a *gpu)
+{
+ unsigned long value;
+ int err;
+
+ err = gk20a_power_up(gpu);
+ if (err < 0)
+ return err;
+
+ value = gk20a_readl(gpu, 0x000000);
+ dev_dbg(gpu->dev, "NV_PMC_BOOT_0: %08lx\n", value);
+ dev_dbg(gpu->dev, " Revision: %lu.%lu\n", (value >> 4) & 0xf,
+ value & 0xf);
+ dev_dbg(gpu->dev, " Device ID: %lx\n", (value >> 12) & 0x3f);
+ dev_dbg(gpu->dev, " Implementation: %lx\n", (value >> 20) & 0xf);
+ dev_dbg(gpu->dev, " Architecture: %lx\n", (value >> 24) & 0x1f);
+ dev_dbg(gpu->dev, " Foundry: %lx\n", (value >> 29) & 0x7);
+
+ value = gk20a_readl(gpu, 0x000004);
+ dev_dbg(gpu->dev, "NV_PMC_BOOT_1: %08lx\n", value);
+
+ value = gk20a_readl(gpu, 0x000008);
+ dev_dbg(gpu->dev, "NV_PMC_BOOT_2: %08lx\n", value);
+
+ value = gk20a_readl(gpu, 0x137250);
+ dev_dbg(gpu->dev, "0x00137250: %08lx\n", value);
+
+ return 0;
+}
+
+static int tegra_gk20a_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct gk20a *gpu;
+ int err;
+
+ dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+ if (dev_get_drvdata(pdev->dev.parent) == NULL) {
+ err = -EPROBE_DEFER;
+ goto out;
+ }
+
+ gpu = devm_kzalloc(&pdev->dev, sizeof(*gpu), GFP_KERNEL);
+ if (!gpu) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ gpu->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpu->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpu->regs)) {
+ err = PTR_ERR(gpu->regs);
+ goto out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ gpu->bar1 = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpu->bar1)) {
+ err = PTR_ERR(gpu->bar1);
+ goto out;
+ }
+
+ gpu->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(gpu->vdd)) {
+ err = PTR_ERR(gpu->vdd);
+ goto out;
+ }
+
+ gpu->rst = devm_reset_control_get(&pdev->dev, "gpu");
+ if (IS_ERR(gpu->rst)) {
+ err = PTR_ERR(gpu->rst);
+ goto out;
+ }
+
+ gpu->clk = devm_clk_get(&pdev->dev, "gpu");
+ if (IS_ERR(gpu->clk)) {
+ err = PTR_ERR(gpu->clk);
+ goto out;
+ }
+
+ err = clk_prepare(gpu->clk);
+ if (err < 0)
+ goto out;
+
+ gpu->clk_pll = devm_clk_get(&pdev->dev, "pll");
+ if (IS_ERR(gpu->clk_pll)) {
+ err = PTR_ERR(gpu->clk_pll);
+ goto out;
+ }
+
+ err = platform_get_irq(pdev, 0);
+ if (err < 0)
+ goto out;
+
+ gpu->irq = err;
+
+ err = platform_get_irq(pdev, 1);
+ if (err < 0)
+ goto out;
+
+ gpu->irq_nostall = err;
+
+ err = devm_request_irq(&pdev->dev, gpu->irq, gk20a_irq, 0, "stall",
+ gpu);
+ if (err < 0)
+ goto out;
+
+ err = devm_request_irq(&pdev->dev, gpu->irq_nostall, gk20a_irq, 0,
+ "non-stall", gpu);
+ if (err < 0)
+ goto out;
+
+ err = clk_prepare(gpu->clk_pll);
+ if (err < 0)
+ goto out;
+
+ err = gk20a_init(gpu);
+ if (err < 0)
+ goto out;
+
+ platform_set_drvdata(pdev, gpu);
+
+out:
+ dev_dbg(&pdev->dev, "< %s() = %d\n", __func__, err);
+ return err;
+}
+
+static int tegra_gk20a_remove(struct platform_device *pdev)
+{
+ struct gk20a *gpu = platform_get_drvdata(pdev);
+ int err = 0;
+
+ dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+ clk_disable_unprepare(gpu->clk_pll);
+ clk_disable_unprepare(gpu->clk);
+
+ dev_dbg(&pdev->dev, "< %s() = %d\n", __func__, err);
+ return err;
+}
+
+static const struct of_device_id tegra_gk20a_match[] = {
+ { .compatible = "nvidia,tegra124-gk20a" },
+ { }
+};
+
+static struct platform_driver tegra_gk20a_driver = {
+ .driver = {
+ .name = "tegra-gk20a",
+ .of_match_table = tegra_gk20a_match,
+ },
+ .probe = tegra_gk20a_probe,
+ .remove = tegra_gk20a_remove,
+};
+module_platform_driver(tegra_gk20a_driver);