All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arto Merilainen <amerilainen@nvidia.com>
To: thierry.reding@gmail.com
Cc: achew@nvidia.com, dnibade@nvidia.com,
	linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
	srasal@nvidia.com, linux-tegra@vger.kernel.org
Subject: [PATCH 3/4] drm/tegra: Add VIC support
Date: Thu, 21 May 2015 16:20:24 +0300	[thread overview]
Message-ID: <1432214425-27137-4-git-send-email-amerilainen@nvidia.com> (raw)
In-Reply-To: <1432214425-27137-1-git-send-email-amerilainen@nvidia.com>

This patch adds support for Video Image Compositor engine which
can be used for 2d operations.

The engine has a microcontroller (Falcon) that acts as a frontend
for the rest of the unit. In order to properly utilize the engine,
the frontend must be booted before pushing any commands.

Signed-off-by: Andrew Chew <achew@nvidia.com>
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
---
 drivers/gpu/drm/tegra/Makefile |   3 +-
 drivers/gpu/drm/tegra/drm.c    |   9 +-
 drivers/gpu/drm/tegra/drm.h    |   1 +
 drivers/gpu/drm/tegra/vic.c    | 593 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/vic.h    | 116 ++++++++
 include/linux/host1x.h         |   1 +
 6 files changed, 721 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/tegra/vic.c
 create mode 100644 drivers/gpu/drm/tegra/vic.h

diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index 2c66a8db9da4..3bc3566e00b6 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -13,6 +13,7 @@ tegra-drm-y := \
 	sor.o \
 	dpaux.o \
 	gr2d.o \
-	gr3d.o
+	gr3d.o \
+	vic.o
 
 obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index bfad15a913a0..d947f5f4d801 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Avionic Design GmbH
- * Copyright (C) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (C) 2012-2015 NVIDIA CORPORATION.  All rights reserved.
  *
  * 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
@@ -1048,6 +1048,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
 	{ .compatible = "nvidia,tegra124-dc", },
 	{ .compatible = "nvidia,tegra124-sor", },
 	{ .compatible = "nvidia,tegra124-hdmi", },
+	{ .compatible = "nvidia,tegra124-vic", },
 	{ /* sentinel */ }
 };
 
@@ -1097,8 +1098,14 @@ static int __init host1x_drm_init(void)
 	if (err < 0)
 		goto unregister_gr2d;
 
+	err = platform_driver_register(&tegra_vic_driver);
+	if (err < 0)
+		goto unregister_gr3d;
+
 	return 0;
 
+unregister_gr3d:
+	platform_driver_unregister(&tegra_gr3d_driver);
 unregister_gr2d:
 	platform_driver_unregister(&tegra_gr2d_driver);
 unregister_dpaux:
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 0e7756e720c5..a9c02a80d6bf 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -275,5 +275,6 @@ extern struct platform_driver tegra_hdmi_driver;
 extern struct platform_driver tegra_dpaux_driver;
 extern struct platform_driver tegra_gr2d_driver;
 extern struct platform_driver tegra_gr3d_driver;
+extern struct platform_driver tegra_vic_driver;
 
 #endif /* HOST1X_DRM_H */
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
new file mode 100644
index 000000000000..b7c5a96697ed
--- /dev/null
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2015, NVIDIA Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <soc/tegra/pmc.h>
+#include <linux/iommu.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "vic.h"
+
+#define VIC_IDLE_TIMEOUT_DEFAULT	10000	/* 10 milliseconds */
+#define VIC_IDLE_CHECK_PERIOD		10	/* 10 usec */
+
+struct vic;
+
+struct vic_config {
+	/* firmware name */
+	char *ucode_name;
+
+	/* class id */
+	u32 class_id;
+
+	/* powergate id */
+	int powergate_id;
+};
+
+struct vic {
+	struct {
+		u32 bin_data_offset;
+		u32 data_offset;
+		u32 data_size;
+		u32 code_offset;
+		u32 size;
+	} os, fce;
+
+	struct tegra_bo *ucode_bo;
+	bool ucode_valid;
+	void *ucode_vaddr;
+
+	bool booted;
+
+	void __iomem *regs;
+	struct tegra_drm_client client;
+	struct host1x_channel *channel;
+	struct iommu_domain *domain;
+	struct device *dev;
+	struct clk *clk;
+	struct reset_control *rst;
+
+	/* Platform configuration */
+	struct vic_config *config;
+
+	/* for firewall - this determines if method 1 should be regarded
+	 * as an address register */
+	bool method_data_is_addr_reg;
+};
+
+static inline struct vic *to_vic(struct tegra_drm_client *client)
+{
+	return container_of(client, struct vic, client);
+}
+
+void vic_writel(struct vic *vic, u32 v, u32 r)
+{
+	writel(v, vic->regs + r);
+}
+
+u32 vic_readl(struct vic *vic, u32 r)
+{
+	return readl(vic->regs + r);
+}
+
+static int vic_wait_idle(struct vic *vic)
+{
+	u32 timeout = VIC_IDLE_TIMEOUT_DEFAULT;
+
+	do {
+		u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, timeout);
+		u32 w = vic_readl(vic, NV_PVIC_FALCON_IDLESTATE);
+
+		if (!w)
+			return 0;
+
+		udelay(VIC_IDLE_CHECK_PERIOD);
+		timeout -= check;
+	} while (timeout);
+
+	dev_err(vic->dev, "vic idle timeout");
+
+	return -ETIMEDOUT;
+}
+
+static int vic_dma_wait_idle(struct vic *vic)
+{
+	u32 timeout = VIC_IDLE_TIMEOUT_DEFAULT;
+
+	do {
+		u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, timeout);
+		u32 dmatrfcmd = vic_readl(vic, NV_PVIC_FALCON_DMATRFCMD);
+
+		if (dmatrfcmd & DMATRFCMD_IDLE)
+			return 0;
+
+		udelay(VIC_IDLE_CHECK_PERIOD);
+		timeout -= check;
+	} while (timeout);
+
+	dev_err(vic->dev, "dma idle timeout");
+
+	return -ETIMEDOUT;
+}
+
+static int vic_dma_pa_to_internal_256b(struct vic *vic, phys_addr_t pa,
+				       u32 internal_offset, bool imem)
+{
+	u32 cmd = DMATRFCMD_SIZE_256B;
+
+	if (imem)
+		cmd |= DMATRFCMD_IMEM;
+
+	vic_writel(vic, DMATRFMOFFS_OFFS(internal_offset),
+		   NV_PVIC_FALCON_DMATRFMOFFS);
+	vic_writel(vic, DMATRFFBOFFS_OFFS(pa),
+		   NV_PVIC_FALCON_DMATRFFBOFFS);
+	vic_writel(vic, cmd, NV_PVIC_FALCON_DMATRFCMD);
+
+	return vic_dma_wait_idle(vic);
+}
+
+static int vic_setup_ucode_image(struct vic *vic,
+				 const struct firmware *ucode_fw)
+{
+	/* image data is little endian. */
+	u32 *ucode_vaddr = vic->ucode_vaddr;
+	struct ucode_v1_vic ucode;
+	int w;
+
+	/* copy the whole thing taking into account endianness */
+	for (w = 0; w < ucode_fw->size / sizeof(u32); w++)
+		ucode_vaddr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
+
+	ucode.bin_header = (struct ucode_bin_header_v1_vic *)ucode_vaddr;
+
+	/* endian problems would show up right here */
+	if (ucode.bin_header->bin_magic != 0x10de) {
+		dev_err(vic->dev, "failed to get firmware magic");
+		return -EINVAL;
+	}
+
+	if (ucode.bin_header->bin_ver != 1) {
+		dev_err(vic->dev, "unsupported firmware version");
+		return -ENOENT;
+	}
+
+	/* shouldn't be bigger than what firmware thinks */
+	if (ucode.bin_header->bin_size > ucode_fw->size) {
+		dev_err(vic->dev, "ucode image size inconsistency");
+		return -EINVAL;
+	}
+
+	ucode.os_header = (struct ucode_os_header_v1_vic *)
+		(((void *)ucode_vaddr) +
+		ucode.bin_header->os_bin_header_offset);
+	vic->os.size = ucode.bin_header->os_bin_size;
+	vic->os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
+	vic->os.code_offset = ucode.os_header->os_code_offset;
+	vic->os.data_offset = ucode.os_header->os_data_offset;
+	vic->os.data_size = ucode.os_header->os_data_size;
+
+	ucode.fce_header = (struct ucode_fce_header_v1_vic *)
+		(((void *)ucode_vaddr) +
+		ucode.bin_header->fce_bin_header_offset);
+	vic->fce.size = ucode.fce_header->fce_ucode_size;
+	vic->fce.data_offset = ucode.bin_header->fce_bin_data_offset;
+
+	return 0;
+}
+
+static int vic_read_ucode(struct vic *vic)
+{
+	struct host1x_client *client = &vic->client.base;
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	const struct firmware *ucode_fw;
+	int err;
+
+	err = request_firmware(&ucode_fw, vic->config->ucode_name, vic->dev);
+	if (err) {
+		dev_err(vic->dev, "failed to get firmware\n");
+		goto err_request_firmware;
+	}
+
+	vic->ucode_bo = tegra_bo_create(dev, ucode_fw->size, 0);
+	if (IS_ERR(vic->ucode_bo)) {
+		dev_err(vic->dev, "dma memory allocation failed");
+		err = PTR_ERR(vic->ucode_bo);
+		goto err_alloc_iova;
+	}
+
+	/* get vaddr for the ucode */
+	if (!vic->ucode_bo->vaddr)
+		vic->ucode_vaddr = vmap(vic->ucode_bo->pages,
+					vic->ucode_bo->num_pages, VM_MAP,
+					pgprot_writecombine(PAGE_KERNEL));
+	else
+		vic->ucode_vaddr = vic->ucode_bo->vaddr;
+
+	err = vic_setup_ucode_image(vic, ucode_fw);
+	if (err) {
+		dev_err(vic->dev, "failed to parse firmware image\n");
+		goto err_setup_ucode_image;
+	}
+
+	vic->ucode_valid = true;
+
+	release_firmware(ucode_fw);
+
+	return 0;
+
+err_setup_ucode_image:
+	drm_gem_object_release(&vic->ucode_bo->gem);
+err_alloc_iova:
+	release_firmware(ucode_fw);
+err_request_firmware:
+	return err;
+}
+
+static int vic_boot(struct device *dev)
+{
+	struct vic *vic = dev_get_drvdata(dev);
+	u32 offset;
+	int err = 0;
+
+	if (vic->booted)
+		return 0;
+
+	if (!vic->ucode_valid) {
+		err = vic_read_ucode(vic);
+		if (err)
+			return err;
+	}
+
+	/* ensure that the engine is in sane state */
+	reset_control_assert(vic->rst);
+	udelay(10);
+	reset_control_deassert(vic->rst);
+
+	/* setup clockgating registers */
+	vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
+			CG_IDLE_CG_EN |
+			CG_WAKEUP_DLY_CNT(4),
+		   NV_PVIC_MISC_PRI_VIC_CG);
+
+	/* service all dma requests */
+	vic_writel(vic, 0, NV_PVIC_FALCON_DMACTL);
+
+	/* setup dma base address */
+	vic_writel(vic, (vic->ucode_bo->paddr + vic->os.bin_data_offset) >> 8,
+		   NV_PVIC_FALCON_DMATRFBASE);
+
+	/* dma ucode data */
+	for (offset = 0; offset < vic->os.data_size; offset += 256)
+		vic_dma_pa_to_internal_256b(vic,
+					     vic->os.data_offset + offset,
+					     offset, false);
+
+	/* dma ucode */
+	vic_dma_pa_to_internal_256b(vic, vic->os.code_offset, 0, true);
+
+	/* setup falcon interrupts and enable interface */
+	vic_writel(vic, IRQMSET_EXT(0xff) |
+			IRQMSET_SWGEN1_SET |
+			IRQMSET_SWGEN0_SET |
+			IRQMSET_EXTERR_SET |
+			IRQMSET_HALT_SET |
+			IRQMSET_WDTMR_SET,
+		   NV_PVIC_FALCON_IRQMSET);
+	vic_writel(vic, IRQDEST_HOST_EXT(0Xff) |
+			IRQDEST_HOST_SWGEN1_HOST |
+			IRQDEST_HOST_SWGEN0_HOST |
+			IRQDEST_HOST_EXTERR_HOST |
+			IRQDEST_HOST_HALT_HOST,
+		    NV_PVIC_FALCON_IRQDEST);
+
+	/* enable method and context switch interfaces */
+	vic_writel(vic, ITFEN_MTHDEN_ENABLE |
+			ITFEN_CTXEN_ENABLE,
+		   NV_PVIC_FALCON_ITFEN);
+
+	/* boot falcon */
+	vic_writel(vic, BOOTVEC_VEC(0), NV_PVIC_FALCON_BOOTVEC);
+	vic_writel(vic, CPUCTL_STARTCPU, NV_PVIC_FALCON_CPUCTL);
+
+	err = vic_wait_idle(vic);
+	if (err != 0) {
+		dev_err(dev, "boot failed due to timeout");
+		return err;
+	}
+
+	/* Set application id and set-up FCE ucode address */
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, 1, NV_PVIC_FALCON_METHOD_1);
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, vic->fce.size, NV_PVIC_FALCON_METHOD_1);
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, (vic->ucode_bo->paddr + vic->fce.data_offset) >> 8,
+		    NV_PVIC_FALCON_METHOD_1);
+
+	err = vic_wait_idle(vic);
+	if (err != 0) {
+		dev_err(dev, "failed to set application id and fce base");
+		return err;
+	}
+
+	vic->booted = true;
+
+	dev_info(dev, "booted");
+
+	return 0;
+}
+
+static int vic_init(struct host1x_client *client)
+{
+	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	struct tegra_drm *tegra = dev->dev_private;
+	struct vic *vic = to_vic(drm);
+	int err;
+
+	if (tegra->domain) {
+		err = iommu_attach_device(tegra->domain, vic->dev);
+		if (err < 0) {
+			dev_err(vic->dev, "failed to attach to domain: %d\n",
+				err);
+			return err;
+		}
+
+		vic->domain = tegra->domain;
+	}
+
+	vic->channel = host1x_channel_request(client->dev);
+	if (!vic->channel)
+		return -ENOMEM;
+
+	client->syncpts[0] = host1x_syncpt_request(client->dev, 0);
+	if (!client->syncpts[0]) {
+		host1x_channel_free(vic->channel);
+		return -ENOMEM;
+	}
+
+	return tegra_drm_register_client(tegra, drm);
+}
+
+static int vic_exit(struct host1x_client *client)
+{
+	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	struct tegra_drm *tegra = dev->dev_private;
+	struct vic *vic = to_vic(drm);
+	int err;
+
+	err = tegra_drm_unregister_client(tegra, drm);
+	if (err < 0)
+		return err;
+
+	host1x_syncpt_free(client->syncpts[0]);
+	host1x_channel_free(vic->channel);
+
+	/* ucode is no longer available. release it */
+	if (vic->ucode_valid) {
+		/* first, ensure that vic is not using it */
+		reset_control_assert(vic->rst);
+		udelay(10);
+		reset_control_deassert(vic->rst);
+
+		/* ..then release the ucode */
+		if (!vic->ucode_bo->vaddr)
+			vunmap(vic->ucode_vaddr);
+		drm_gem_object_release(&vic->ucode_bo->gem);
+		vic->ucode_valid = false;
+	}
+
+	if (vic->domain) {
+		iommu_detach_device(vic->domain, vic->dev);
+		vic->domain = NULL;
+	}
+
+	return 0;
+}
+
+static const struct host1x_client_ops vic_client_ops = {
+	.init = vic_init,
+	.exit = vic_exit,
+};
+
+static int vic_open_channel(struct tegra_drm_client *client,
+			     struct tegra_drm_context *context)
+{
+	struct vic *vic = to_vic(client);
+	int err;
+
+	err = vic_boot(vic->dev);
+	if (err)
+		return err;
+
+	context->channel = host1x_channel_get(vic->channel);
+	if (!context->channel)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void vic_close_channel(struct tegra_drm_context *context)
+{
+	host1x_channel_put(context->channel);
+}
+
+static int vic_is_addr_reg(struct device *dev, u32 class, u32 offset, u32 val)
+{
+	struct vic *vic = dev_get_drvdata(dev);
+
+	/* handle host class */
+	if (class == HOST1X_CLASS_HOST1X) {
+		if (offset == 0x2b)
+			return true;
+		return false;
+	}
+
+	/* write targets towards method 1. check stashed value */
+	if (offset == NV_PVIC_FALCON_METHOD_1 >> 2)
+		return vic->method_data_is_addr_reg;
+
+	/* write targets to method 0. determine if the method takes an
+	 * address as a parameter */
+	if (offset == NV_PVIC_FALCON_METHOD_0 >> 2) {
+		u32 method = val << 2;
+
+		if ((method >= 0x400 && method <= 0x5dc) ||
+		    (method >= 0x720 && method <= 0x738))
+			vic->method_data_is_addr_reg = true;
+		else
+			vic->method_data_is_addr_reg = false;
+	}
+
+	/* default to false */
+	return false;
+}
+
+static const struct tegra_drm_client_ops vic_ops = {
+	.open_channel = vic_open_channel,
+	.close_channel = vic_close_channel,
+	.is_addr_reg = vic_is_addr_reg,
+	.submit = tegra_drm_submit,
+};
+
+static const struct vic_config vic_t124_config = {
+	.ucode_name = "vic03_ucode.bin",
+	.class_id = HOST1X_CLASS_VIC,
+	.powergate_id = TEGRA_POWERGATE_VIC,
+};
+
+static const struct of_device_id vic_match[] = {
+	{ .compatible = "nvidia,tegra124-vic",
+		.data = &vic_t124_config },
+	{ },
+};
+
+static int vic_probe(struct platform_device *pdev)
+{
+	struct vic_config *vic_config = NULL;
+	struct device *dev = &pdev->dev;
+	struct host1x_syncpt **syncpts;
+	struct resource *regs;
+	struct vic *vic;
+	int err;
+
+	if (dev->of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_device(vic_match, dev);
+		if (match)
+			vic_config = (struct vic_config *)match->data;
+		else
+			return -ENXIO;
+	}
+
+	vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL);
+	if (!vic)
+		return -ENOMEM;
+
+	syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
+	if (!syncpts)
+		return -ENOMEM;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(&pdev->dev, "failed to get registers\n");
+		return -ENXIO;
+	}
+
+	vic->regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(vic->regs))
+		return PTR_ERR(vic->regs);
+
+	vic->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(vic->clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(vic->clk);
+	}
+
+	vic->rst = devm_reset_control_get(dev, "vic03");
+	if (IS_ERR(vic->rst)) {
+		dev_err(&pdev->dev, "cannot get reset\n");
+		return PTR_ERR(vic->rst);
+	}
+
+	platform_set_drvdata(pdev, vic);
+
+	INIT_LIST_HEAD(&vic->client.base.list);
+	vic->client.base.ops = &vic_client_ops;
+	vic->client.base.dev = dev;
+	vic->client.base.class = vic_config->class_id;
+	vic->client.base.syncpts = syncpts;
+	vic->client.base.num_syncpts = 1;
+	vic->dev = dev;
+	vic->config = vic_config;
+
+	INIT_LIST_HEAD(&vic->client.list);
+	vic->client.ops = &vic_ops;
+
+	err = tegra_powergate_sequence_power_up(vic->config->powergate_id,
+						vic->clk, vic->rst);
+	if (err) {
+		dev_err(dev, "cannot turn on the device\n");
+		return err;
+	}
+
+	err = host1x_client_register(&vic->client.base);
+	if (err < 0) {
+		dev_err(dev, "failed to register host1x client: %d\n", err);
+		clk_disable_unprepare(vic->clk);
+		tegra_powergate_power_off(vic->config->powergate_id);
+		platform_set_drvdata(pdev, NULL);
+		return err;
+	}
+
+	dev_info(&pdev->dev, "initialized");
+
+	return 0;
+}
+
+static int vic_remove(struct platform_device *pdev)
+{
+	struct vic *vic = platform_get_drvdata(pdev);
+	int err;
+
+	err = host1x_client_unregister(&vic->client.base);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+			err);
+		return err;
+	}
+
+	clk_disable_unprepare(vic->clk);
+	tegra_powergate_power_off(vic->config->powergate_id);
+
+	return 0;
+}
+
+struct platform_driver tegra_vic_driver = {
+	.driver = {
+		.name = "tegra-vic",
+		.of_match_table = vic_match,
+	},
+	.probe = vic_probe,
+	.remove = vic_remove,
+};
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h
new file mode 100644
index 000000000000..65ca38a8da88
--- /dev/null
+++ b/drivers/gpu/drm/tegra/vic.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, NVIDIA Corporation.
+ *
+ * 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.
+ */
+
+#ifndef TEGRA_VIC_H
+#define TEGRA_VIC_H
+
+#include <linux/types.h>
+#include <linux/dma-attrs.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+
+struct ucode_bin_header_v1_vic {
+	u32 bin_magic;        /* 0x10de */
+	u32 bin_ver;          /* cya, versioning of bin format (1) */
+	u32 bin_size;         /* entire image size including this header */
+	u32 os_bin_header_offset;
+	u32 os_bin_data_offset;
+	u32 os_bin_size;
+	u32 fce_bin_header_offset;
+	u32 fce_bin_data_offset;
+	u32 fce_bin_size;
+};
+
+struct ucode_os_code_header_v1_vic {
+	u32 offset;
+	u32 size;
+};
+
+struct ucode_os_header_v1_vic {
+	u32 os_code_offset;
+	u32 os_code_size;
+	u32 os_data_offset;
+	u32 os_data_size;
+	u32 num_apps;
+	struct ucode_os_code_header_v1_vic *app_code;
+	struct ucode_os_code_header_v1_vic *app_data;
+	u32 *os_ovl_offset;
+	u32 *of_ovl_size;
+};
+
+struct ucode_fce_header_v1_vic {
+	u32 fce_ucode_offset;
+	u32 fce_ucode_buffer_size;
+	u32 fce_ucode_size;
+};
+
+struct ucode_v1_vic {
+	struct ucode_bin_header_v1_vic *bin_header;
+	struct ucode_os_header_v1_vic  *os_header;
+	struct ucode_fce_header_v1_vic *fce_header;
+};
+
+/* VIC methods */
+#define NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID	0x00000200
+#define NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE	0x0000071C
+#define NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET	0x0000072C
+
+/* VIC registers */
+
+#define NV_PVIC_FALCON_METHOD_0			0x00000040
+#define NV_PVIC_FALCON_METHOD_1			0x00000044
+
+#define NV_PVIC_FALCON_IRQMSET			0x00001010
+#define IRQMSET_WDTMR_SET			(1 << 1)
+#define IRQMSET_HALT_SET			(1 << 4)
+#define IRQMSET_EXTERR_SET			(1 << 5)
+#define IRQMSET_SWGEN0_SET			(1 << 6)
+#define IRQMSET_SWGEN1_SET			(1 << 7)
+#define IRQMSET_EXT(val)			((val & 0xff) << 8)
+
+#define NV_PVIC_FALCON_IRQDEST			0x0000101c
+#define IRQDEST_HOST_HALT_HOST			(1 << 4)
+#define IRQDEST_HOST_EXTERR_HOST		(1 << 5)
+#define IRQDEST_HOST_SWGEN0_HOST		(1 << 6)
+#define IRQDEST_HOST_SWGEN1_HOST		(1 << 7)
+#define IRQDEST_HOST_EXT(val)			((val & 0xff) << 8)
+
+#define NV_PVIC_FALCON_ITFEN			0x00001048
+#define ITFEN_CTXEN_ENABLE			(1 << 0)
+#define ITFEN_MTHDEN_ENABLE			(1 << 1)
+
+#define NV_PVIC_FALCON_IDLESTATE		0x0000104c
+
+#define NV_PVIC_FALCON_CPUCTL			0x00001100
+#define CPUCTL_STARTCPU				(1 << 1)
+
+#define NV_PVIC_FALCON_BOOTVEC			0x00001104
+#define BOOTVEC_VEC(val)			((val & 0xffffffff) << 0)
+
+#define NV_PVIC_FALCON_DMACTL			0x0000110c
+
+#define NV_PVIC_FALCON_DMATRFBASE		0x00001110
+
+#define NV_PVIC_FALCON_DMATRFMOFFS		0x00001114
+#define DMATRFMOFFS_OFFS(val)			((val & 0xffff) << 0)
+
+#define NV_PVIC_FALCON_DMATRFCMD		0x00001118
+#define DMATRFCMD_IDLE				(1 << 1)
+#define DMATRFCMD_IMEM				(1 << 4)
+#define DMATRFCMD_SIZE_256B			(6 << 8)
+
+#define NV_PVIC_FALCON_DMATRFFBOFFS		0x0000111c
+#define DMATRFFBOFFS_OFFS(val)			((val & 0xffffffff) << 0)
+
+#define NV_PVIC_MISC_PRI_VIC_CG			0x000016d0
+#define CG_IDLE_CG_DLY_CNT(val)			((val & 0x3f) << 0)
+#define CG_IDLE_CG_EN				(1 << 6)
+#define CG_WAKEUP_DLY_CNT(val)			((val & 0xf) << 16)
+
+
+#endif /* TEGRA_VIC_H */
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index fc86ced77e76..a006dad00009 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -26,6 +26,7 @@ enum host1x_class {
 	HOST1X_CLASS_HOST1X = 0x1,
 	HOST1X_CLASS_GR2D = 0x51,
 	HOST1X_CLASS_GR2D_SB = 0x52,
+	HOST1X_CLASS_VIC = 0x5D,
 	HOST1X_CLASS_GR3D = 0x60,
 };
 
-- 
1.8.1.5

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

WARNING: multiple messages have this Message-ID (diff)
From: Arto Merilainen <amerilainen@nvidia.com>
To: thierry.reding@gmail.com
Cc: linux-tegra@vger.kernel.org, dri-devel@lists.freedesktop.org,
	linux-kernel@vger.kernel.org, achew@nvidia.com,
	srasal@nvidia.com, dnibade@nvidia.com
Subject: [PATCH 3/4] drm/tegra: Add VIC support
Date: Thu, 21 May 2015 16:20:24 +0300	[thread overview]
Message-ID: <1432214425-27137-4-git-send-email-amerilainen@nvidia.com> (raw)
In-Reply-To: <1432214425-27137-1-git-send-email-amerilainen@nvidia.com>

This patch adds support for Video Image Compositor engine which
can be used for 2d operations.

The engine has a microcontroller (Falcon) that acts as a frontend
for the rest of the unit. In order to properly utilize the engine,
the frontend must be booted before pushing any commands.

Signed-off-by: Andrew Chew <achew@nvidia.com>
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
---
 drivers/gpu/drm/tegra/Makefile |   3 +-
 drivers/gpu/drm/tegra/drm.c    |   9 +-
 drivers/gpu/drm/tegra/drm.h    |   1 +
 drivers/gpu/drm/tegra/vic.c    | 593 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/vic.h    | 116 ++++++++
 include/linux/host1x.h         |   1 +
 6 files changed, 721 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/tegra/vic.c
 create mode 100644 drivers/gpu/drm/tegra/vic.h

diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index 2c66a8db9da4..3bc3566e00b6 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -13,6 +13,7 @@ tegra-drm-y := \
 	sor.o \
 	dpaux.o \
 	gr2d.o \
-	gr3d.o
+	gr3d.o \
+	vic.o
 
 obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index bfad15a913a0..d947f5f4d801 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Avionic Design GmbH
- * Copyright (C) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (C) 2012-2015 NVIDIA CORPORATION.  All rights reserved.
  *
  * 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
@@ -1048,6 +1048,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
 	{ .compatible = "nvidia,tegra124-dc", },
 	{ .compatible = "nvidia,tegra124-sor", },
 	{ .compatible = "nvidia,tegra124-hdmi", },
+	{ .compatible = "nvidia,tegra124-vic", },
 	{ /* sentinel */ }
 };
 
@@ -1097,8 +1098,14 @@ static int __init host1x_drm_init(void)
 	if (err < 0)
 		goto unregister_gr2d;
 
+	err = platform_driver_register(&tegra_vic_driver);
+	if (err < 0)
+		goto unregister_gr3d;
+
 	return 0;
 
+unregister_gr3d:
+	platform_driver_unregister(&tegra_gr3d_driver);
 unregister_gr2d:
 	platform_driver_unregister(&tegra_gr2d_driver);
 unregister_dpaux:
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 0e7756e720c5..a9c02a80d6bf 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -275,5 +275,6 @@ extern struct platform_driver tegra_hdmi_driver;
 extern struct platform_driver tegra_dpaux_driver;
 extern struct platform_driver tegra_gr2d_driver;
 extern struct platform_driver tegra_gr3d_driver;
+extern struct platform_driver tegra_vic_driver;
 
 #endif /* HOST1X_DRM_H */
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
new file mode 100644
index 000000000000..b7c5a96697ed
--- /dev/null
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2015, NVIDIA Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <soc/tegra/pmc.h>
+#include <linux/iommu.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "vic.h"
+
+#define VIC_IDLE_TIMEOUT_DEFAULT	10000	/* 10 milliseconds */
+#define VIC_IDLE_CHECK_PERIOD		10	/* 10 usec */
+
+struct vic;
+
+struct vic_config {
+	/* firmware name */
+	char *ucode_name;
+
+	/* class id */
+	u32 class_id;
+
+	/* powergate id */
+	int powergate_id;
+};
+
+struct vic {
+	struct {
+		u32 bin_data_offset;
+		u32 data_offset;
+		u32 data_size;
+		u32 code_offset;
+		u32 size;
+	} os, fce;
+
+	struct tegra_bo *ucode_bo;
+	bool ucode_valid;
+	void *ucode_vaddr;
+
+	bool booted;
+
+	void __iomem *regs;
+	struct tegra_drm_client client;
+	struct host1x_channel *channel;
+	struct iommu_domain *domain;
+	struct device *dev;
+	struct clk *clk;
+	struct reset_control *rst;
+
+	/* Platform configuration */
+	struct vic_config *config;
+
+	/* for firewall - this determines if method 1 should be regarded
+	 * as an address register */
+	bool method_data_is_addr_reg;
+};
+
+static inline struct vic *to_vic(struct tegra_drm_client *client)
+{
+	return container_of(client, struct vic, client);
+}
+
+void vic_writel(struct vic *vic, u32 v, u32 r)
+{
+	writel(v, vic->regs + r);
+}
+
+u32 vic_readl(struct vic *vic, u32 r)
+{
+	return readl(vic->regs + r);
+}
+
+static int vic_wait_idle(struct vic *vic)
+{
+	u32 timeout = VIC_IDLE_TIMEOUT_DEFAULT;
+
+	do {
+		u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, timeout);
+		u32 w = vic_readl(vic, NV_PVIC_FALCON_IDLESTATE);
+
+		if (!w)
+			return 0;
+
+		udelay(VIC_IDLE_CHECK_PERIOD);
+		timeout -= check;
+	} while (timeout);
+
+	dev_err(vic->dev, "vic idle timeout");
+
+	return -ETIMEDOUT;
+}
+
+static int vic_dma_wait_idle(struct vic *vic)
+{
+	u32 timeout = VIC_IDLE_TIMEOUT_DEFAULT;
+
+	do {
+		u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, timeout);
+		u32 dmatrfcmd = vic_readl(vic, NV_PVIC_FALCON_DMATRFCMD);
+
+		if (dmatrfcmd & DMATRFCMD_IDLE)
+			return 0;
+
+		udelay(VIC_IDLE_CHECK_PERIOD);
+		timeout -= check;
+	} while (timeout);
+
+	dev_err(vic->dev, "dma idle timeout");
+
+	return -ETIMEDOUT;
+}
+
+static int vic_dma_pa_to_internal_256b(struct vic *vic, phys_addr_t pa,
+				       u32 internal_offset, bool imem)
+{
+	u32 cmd = DMATRFCMD_SIZE_256B;
+
+	if (imem)
+		cmd |= DMATRFCMD_IMEM;
+
+	vic_writel(vic, DMATRFMOFFS_OFFS(internal_offset),
+		   NV_PVIC_FALCON_DMATRFMOFFS);
+	vic_writel(vic, DMATRFFBOFFS_OFFS(pa),
+		   NV_PVIC_FALCON_DMATRFFBOFFS);
+	vic_writel(vic, cmd, NV_PVIC_FALCON_DMATRFCMD);
+
+	return vic_dma_wait_idle(vic);
+}
+
+static int vic_setup_ucode_image(struct vic *vic,
+				 const struct firmware *ucode_fw)
+{
+	/* image data is little endian. */
+	u32 *ucode_vaddr = vic->ucode_vaddr;
+	struct ucode_v1_vic ucode;
+	int w;
+
+	/* copy the whole thing taking into account endianness */
+	for (w = 0; w < ucode_fw->size / sizeof(u32); w++)
+		ucode_vaddr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
+
+	ucode.bin_header = (struct ucode_bin_header_v1_vic *)ucode_vaddr;
+
+	/* endian problems would show up right here */
+	if (ucode.bin_header->bin_magic != 0x10de) {
+		dev_err(vic->dev, "failed to get firmware magic");
+		return -EINVAL;
+	}
+
+	if (ucode.bin_header->bin_ver != 1) {
+		dev_err(vic->dev, "unsupported firmware version");
+		return -ENOENT;
+	}
+
+	/* shouldn't be bigger than what firmware thinks */
+	if (ucode.bin_header->bin_size > ucode_fw->size) {
+		dev_err(vic->dev, "ucode image size inconsistency");
+		return -EINVAL;
+	}
+
+	ucode.os_header = (struct ucode_os_header_v1_vic *)
+		(((void *)ucode_vaddr) +
+		ucode.bin_header->os_bin_header_offset);
+	vic->os.size = ucode.bin_header->os_bin_size;
+	vic->os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
+	vic->os.code_offset = ucode.os_header->os_code_offset;
+	vic->os.data_offset = ucode.os_header->os_data_offset;
+	vic->os.data_size = ucode.os_header->os_data_size;
+
+	ucode.fce_header = (struct ucode_fce_header_v1_vic *)
+		(((void *)ucode_vaddr) +
+		ucode.bin_header->fce_bin_header_offset);
+	vic->fce.size = ucode.fce_header->fce_ucode_size;
+	vic->fce.data_offset = ucode.bin_header->fce_bin_data_offset;
+
+	return 0;
+}
+
+static int vic_read_ucode(struct vic *vic)
+{
+	struct host1x_client *client = &vic->client.base;
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	const struct firmware *ucode_fw;
+	int err;
+
+	err = request_firmware(&ucode_fw, vic->config->ucode_name, vic->dev);
+	if (err) {
+		dev_err(vic->dev, "failed to get firmware\n");
+		goto err_request_firmware;
+	}
+
+	vic->ucode_bo = tegra_bo_create(dev, ucode_fw->size, 0);
+	if (IS_ERR(vic->ucode_bo)) {
+		dev_err(vic->dev, "dma memory allocation failed");
+		err = PTR_ERR(vic->ucode_bo);
+		goto err_alloc_iova;
+	}
+
+	/* get vaddr for the ucode */
+	if (!vic->ucode_bo->vaddr)
+		vic->ucode_vaddr = vmap(vic->ucode_bo->pages,
+					vic->ucode_bo->num_pages, VM_MAP,
+					pgprot_writecombine(PAGE_KERNEL));
+	else
+		vic->ucode_vaddr = vic->ucode_bo->vaddr;
+
+	err = vic_setup_ucode_image(vic, ucode_fw);
+	if (err) {
+		dev_err(vic->dev, "failed to parse firmware image\n");
+		goto err_setup_ucode_image;
+	}
+
+	vic->ucode_valid = true;
+
+	release_firmware(ucode_fw);
+
+	return 0;
+
+err_setup_ucode_image:
+	drm_gem_object_release(&vic->ucode_bo->gem);
+err_alloc_iova:
+	release_firmware(ucode_fw);
+err_request_firmware:
+	return err;
+}
+
+static int vic_boot(struct device *dev)
+{
+	struct vic *vic = dev_get_drvdata(dev);
+	u32 offset;
+	int err = 0;
+
+	if (vic->booted)
+		return 0;
+
+	if (!vic->ucode_valid) {
+		err = vic_read_ucode(vic);
+		if (err)
+			return err;
+	}
+
+	/* ensure that the engine is in sane state */
+	reset_control_assert(vic->rst);
+	udelay(10);
+	reset_control_deassert(vic->rst);
+
+	/* setup clockgating registers */
+	vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
+			CG_IDLE_CG_EN |
+			CG_WAKEUP_DLY_CNT(4),
+		   NV_PVIC_MISC_PRI_VIC_CG);
+
+	/* service all dma requests */
+	vic_writel(vic, 0, NV_PVIC_FALCON_DMACTL);
+
+	/* setup dma base address */
+	vic_writel(vic, (vic->ucode_bo->paddr + vic->os.bin_data_offset) >> 8,
+		   NV_PVIC_FALCON_DMATRFBASE);
+
+	/* dma ucode data */
+	for (offset = 0; offset < vic->os.data_size; offset += 256)
+		vic_dma_pa_to_internal_256b(vic,
+					     vic->os.data_offset + offset,
+					     offset, false);
+
+	/* dma ucode */
+	vic_dma_pa_to_internal_256b(vic, vic->os.code_offset, 0, true);
+
+	/* setup falcon interrupts and enable interface */
+	vic_writel(vic, IRQMSET_EXT(0xff) |
+			IRQMSET_SWGEN1_SET |
+			IRQMSET_SWGEN0_SET |
+			IRQMSET_EXTERR_SET |
+			IRQMSET_HALT_SET |
+			IRQMSET_WDTMR_SET,
+		   NV_PVIC_FALCON_IRQMSET);
+	vic_writel(vic, IRQDEST_HOST_EXT(0Xff) |
+			IRQDEST_HOST_SWGEN1_HOST |
+			IRQDEST_HOST_SWGEN0_HOST |
+			IRQDEST_HOST_EXTERR_HOST |
+			IRQDEST_HOST_HALT_HOST,
+		    NV_PVIC_FALCON_IRQDEST);
+
+	/* enable method and context switch interfaces */
+	vic_writel(vic, ITFEN_MTHDEN_ENABLE |
+			ITFEN_CTXEN_ENABLE,
+		   NV_PVIC_FALCON_ITFEN);
+
+	/* boot falcon */
+	vic_writel(vic, BOOTVEC_VEC(0), NV_PVIC_FALCON_BOOTVEC);
+	vic_writel(vic, CPUCTL_STARTCPU, NV_PVIC_FALCON_CPUCTL);
+
+	err = vic_wait_idle(vic);
+	if (err != 0) {
+		dev_err(dev, "boot failed due to timeout");
+		return err;
+	}
+
+	/* Set application id and set-up FCE ucode address */
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, 1, NV_PVIC_FALCON_METHOD_1);
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, vic->fce.size, NV_PVIC_FALCON_METHOD_1);
+	vic_writel(vic, NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET >> 2,
+		    NV_PVIC_FALCON_METHOD_0);
+	vic_writel(vic, (vic->ucode_bo->paddr + vic->fce.data_offset) >> 8,
+		    NV_PVIC_FALCON_METHOD_1);
+
+	err = vic_wait_idle(vic);
+	if (err != 0) {
+		dev_err(dev, "failed to set application id and fce base");
+		return err;
+	}
+
+	vic->booted = true;
+
+	dev_info(dev, "booted");
+
+	return 0;
+}
+
+static int vic_init(struct host1x_client *client)
+{
+	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	struct tegra_drm *tegra = dev->dev_private;
+	struct vic *vic = to_vic(drm);
+	int err;
+
+	if (tegra->domain) {
+		err = iommu_attach_device(tegra->domain, vic->dev);
+		if (err < 0) {
+			dev_err(vic->dev, "failed to attach to domain: %d\n",
+				err);
+			return err;
+		}
+
+		vic->domain = tegra->domain;
+	}
+
+	vic->channel = host1x_channel_request(client->dev);
+	if (!vic->channel)
+		return -ENOMEM;
+
+	client->syncpts[0] = host1x_syncpt_request(client->dev, 0);
+	if (!client->syncpts[0]) {
+		host1x_channel_free(vic->channel);
+		return -ENOMEM;
+	}
+
+	return tegra_drm_register_client(tegra, drm);
+}
+
+static int vic_exit(struct host1x_client *client)
+{
+	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct drm_device *dev = dev_get_drvdata(client->parent);
+	struct tegra_drm *tegra = dev->dev_private;
+	struct vic *vic = to_vic(drm);
+	int err;
+
+	err = tegra_drm_unregister_client(tegra, drm);
+	if (err < 0)
+		return err;
+
+	host1x_syncpt_free(client->syncpts[0]);
+	host1x_channel_free(vic->channel);
+
+	/* ucode is no longer available. release it */
+	if (vic->ucode_valid) {
+		/* first, ensure that vic is not using it */
+		reset_control_assert(vic->rst);
+		udelay(10);
+		reset_control_deassert(vic->rst);
+
+		/* ..then release the ucode */
+		if (!vic->ucode_bo->vaddr)
+			vunmap(vic->ucode_vaddr);
+		drm_gem_object_release(&vic->ucode_bo->gem);
+		vic->ucode_valid = false;
+	}
+
+	if (vic->domain) {
+		iommu_detach_device(vic->domain, vic->dev);
+		vic->domain = NULL;
+	}
+
+	return 0;
+}
+
+static const struct host1x_client_ops vic_client_ops = {
+	.init = vic_init,
+	.exit = vic_exit,
+};
+
+static int vic_open_channel(struct tegra_drm_client *client,
+			     struct tegra_drm_context *context)
+{
+	struct vic *vic = to_vic(client);
+	int err;
+
+	err = vic_boot(vic->dev);
+	if (err)
+		return err;
+
+	context->channel = host1x_channel_get(vic->channel);
+	if (!context->channel)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void vic_close_channel(struct tegra_drm_context *context)
+{
+	host1x_channel_put(context->channel);
+}
+
+static int vic_is_addr_reg(struct device *dev, u32 class, u32 offset, u32 val)
+{
+	struct vic *vic = dev_get_drvdata(dev);
+
+	/* handle host class */
+	if (class == HOST1X_CLASS_HOST1X) {
+		if (offset == 0x2b)
+			return true;
+		return false;
+	}
+
+	/* write targets towards method 1. check stashed value */
+	if (offset == NV_PVIC_FALCON_METHOD_1 >> 2)
+		return vic->method_data_is_addr_reg;
+
+	/* write targets to method 0. determine if the method takes an
+	 * address as a parameter */
+	if (offset == NV_PVIC_FALCON_METHOD_0 >> 2) {
+		u32 method = val << 2;
+
+		if ((method >= 0x400 && method <= 0x5dc) ||
+		    (method >= 0x720 && method <= 0x738))
+			vic->method_data_is_addr_reg = true;
+		else
+			vic->method_data_is_addr_reg = false;
+	}
+
+	/* default to false */
+	return false;
+}
+
+static const struct tegra_drm_client_ops vic_ops = {
+	.open_channel = vic_open_channel,
+	.close_channel = vic_close_channel,
+	.is_addr_reg = vic_is_addr_reg,
+	.submit = tegra_drm_submit,
+};
+
+static const struct vic_config vic_t124_config = {
+	.ucode_name = "vic03_ucode.bin",
+	.class_id = HOST1X_CLASS_VIC,
+	.powergate_id = TEGRA_POWERGATE_VIC,
+};
+
+static const struct of_device_id vic_match[] = {
+	{ .compatible = "nvidia,tegra124-vic",
+		.data = &vic_t124_config },
+	{ },
+};
+
+static int vic_probe(struct platform_device *pdev)
+{
+	struct vic_config *vic_config = NULL;
+	struct device *dev = &pdev->dev;
+	struct host1x_syncpt **syncpts;
+	struct resource *regs;
+	struct vic *vic;
+	int err;
+
+	if (dev->of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_device(vic_match, dev);
+		if (match)
+			vic_config = (struct vic_config *)match->data;
+		else
+			return -ENXIO;
+	}
+
+	vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL);
+	if (!vic)
+		return -ENOMEM;
+
+	syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
+	if (!syncpts)
+		return -ENOMEM;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(&pdev->dev, "failed to get registers\n");
+		return -ENXIO;
+	}
+
+	vic->regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(vic->regs))
+		return PTR_ERR(vic->regs);
+
+	vic->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(vic->clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(vic->clk);
+	}
+
+	vic->rst = devm_reset_control_get(dev, "vic03");
+	if (IS_ERR(vic->rst)) {
+		dev_err(&pdev->dev, "cannot get reset\n");
+		return PTR_ERR(vic->rst);
+	}
+
+	platform_set_drvdata(pdev, vic);
+
+	INIT_LIST_HEAD(&vic->client.base.list);
+	vic->client.base.ops = &vic_client_ops;
+	vic->client.base.dev = dev;
+	vic->client.base.class = vic_config->class_id;
+	vic->client.base.syncpts = syncpts;
+	vic->client.base.num_syncpts = 1;
+	vic->dev = dev;
+	vic->config = vic_config;
+
+	INIT_LIST_HEAD(&vic->client.list);
+	vic->client.ops = &vic_ops;
+
+	err = tegra_powergate_sequence_power_up(vic->config->powergate_id,
+						vic->clk, vic->rst);
+	if (err) {
+		dev_err(dev, "cannot turn on the device\n");
+		return err;
+	}
+
+	err = host1x_client_register(&vic->client.base);
+	if (err < 0) {
+		dev_err(dev, "failed to register host1x client: %d\n", err);
+		clk_disable_unprepare(vic->clk);
+		tegra_powergate_power_off(vic->config->powergate_id);
+		platform_set_drvdata(pdev, NULL);
+		return err;
+	}
+
+	dev_info(&pdev->dev, "initialized");
+
+	return 0;
+}
+
+static int vic_remove(struct platform_device *pdev)
+{
+	struct vic *vic = platform_get_drvdata(pdev);
+	int err;
+
+	err = host1x_client_unregister(&vic->client.base);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+			err);
+		return err;
+	}
+
+	clk_disable_unprepare(vic->clk);
+	tegra_powergate_power_off(vic->config->powergate_id);
+
+	return 0;
+}
+
+struct platform_driver tegra_vic_driver = {
+	.driver = {
+		.name = "tegra-vic",
+		.of_match_table = vic_match,
+	},
+	.probe = vic_probe,
+	.remove = vic_remove,
+};
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h
new file mode 100644
index 000000000000..65ca38a8da88
--- /dev/null
+++ b/drivers/gpu/drm/tegra/vic.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, NVIDIA Corporation.
+ *
+ * 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.
+ */
+
+#ifndef TEGRA_VIC_H
+#define TEGRA_VIC_H
+
+#include <linux/types.h>
+#include <linux/dma-attrs.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+
+struct ucode_bin_header_v1_vic {
+	u32 bin_magic;        /* 0x10de */
+	u32 bin_ver;          /* cya, versioning of bin format (1) */
+	u32 bin_size;         /* entire image size including this header */
+	u32 os_bin_header_offset;
+	u32 os_bin_data_offset;
+	u32 os_bin_size;
+	u32 fce_bin_header_offset;
+	u32 fce_bin_data_offset;
+	u32 fce_bin_size;
+};
+
+struct ucode_os_code_header_v1_vic {
+	u32 offset;
+	u32 size;
+};
+
+struct ucode_os_header_v1_vic {
+	u32 os_code_offset;
+	u32 os_code_size;
+	u32 os_data_offset;
+	u32 os_data_size;
+	u32 num_apps;
+	struct ucode_os_code_header_v1_vic *app_code;
+	struct ucode_os_code_header_v1_vic *app_data;
+	u32 *os_ovl_offset;
+	u32 *of_ovl_size;
+};
+
+struct ucode_fce_header_v1_vic {
+	u32 fce_ucode_offset;
+	u32 fce_ucode_buffer_size;
+	u32 fce_ucode_size;
+};
+
+struct ucode_v1_vic {
+	struct ucode_bin_header_v1_vic *bin_header;
+	struct ucode_os_header_v1_vic  *os_header;
+	struct ucode_fce_header_v1_vic *fce_header;
+};
+
+/* VIC methods */
+#define NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID	0x00000200
+#define NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE	0x0000071C
+#define NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET	0x0000072C
+
+/* VIC registers */
+
+#define NV_PVIC_FALCON_METHOD_0			0x00000040
+#define NV_PVIC_FALCON_METHOD_1			0x00000044
+
+#define NV_PVIC_FALCON_IRQMSET			0x00001010
+#define IRQMSET_WDTMR_SET			(1 << 1)
+#define IRQMSET_HALT_SET			(1 << 4)
+#define IRQMSET_EXTERR_SET			(1 << 5)
+#define IRQMSET_SWGEN0_SET			(1 << 6)
+#define IRQMSET_SWGEN1_SET			(1 << 7)
+#define IRQMSET_EXT(val)			((val & 0xff) << 8)
+
+#define NV_PVIC_FALCON_IRQDEST			0x0000101c
+#define IRQDEST_HOST_HALT_HOST			(1 << 4)
+#define IRQDEST_HOST_EXTERR_HOST		(1 << 5)
+#define IRQDEST_HOST_SWGEN0_HOST		(1 << 6)
+#define IRQDEST_HOST_SWGEN1_HOST		(1 << 7)
+#define IRQDEST_HOST_EXT(val)			((val & 0xff) << 8)
+
+#define NV_PVIC_FALCON_ITFEN			0x00001048
+#define ITFEN_CTXEN_ENABLE			(1 << 0)
+#define ITFEN_MTHDEN_ENABLE			(1 << 1)
+
+#define NV_PVIC_FALCON_IDLESTATE		0x0000104c
+
+#define NV_PVIC_FALCON_CPUCTL			0x00001100
+#define CPUCTL_STARTCPU				(1 << 1)
+
+#define NV_PVIC_FALCON_BOOTVEC			0x00001104
+#define BOOTVEC_VEC(val)			((val & 0xffffffff) << 0)
+
+#define NV_PVIC_FALCON_DMACTL			0x0000110c
+
+#define NV_PVIC_FALCON_DMATRFBASE		0x00001110
+
+#define NV_PVIC_FALCON_DMATRFMOFFS		0x00001114
+#define DMATRFMOFFS_OFFS(val)			((val & 0xffff) << 0)
+
+#define NV_PVIC_FALCON_DMATRFCMD		0x00001118
+#define DMATRFCMD_IDLE				(1 << 1)
+#define DMATRFCMD_IMEM				(1 << 4)
+#define DMATRFCMD_SIZE_256B			(6 << 8)
+
+#define NV_PVIC_FALCON_DMATRFFBOFFS		0x0000111c
+#define DMATRFFBOFFS_OFFS(val)			((val & 0xffffffff) << 0)
+
+#define NV_PVIC_MISC_PRI_VIC_CG			0x000016d0
+#define CG_IDLE_CG_DLY_CNT(val)			((val & 0x3f) << 0)
+#define CG_IDLE_CG_EN				(1 << 6)
+#define CG_WAKEUP_DLY_CNT(val)			((val & 0xf) << 16)
+
+
+#endif /* TEGRA_VIC_H */
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index fc86ced77e76..a006dad00009 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -26,6 +26,7 @@ enum host1x_class {
 	HOST1X_CLASS_HOST1X = 0x1,
 	HOST1X_CLASS_GR2D = 0x51,
 	HOST1X_CLASS_GR2D_SB = 0x52,
+	HOST1X_CLASS_VIC = 0x5D,
 	HOST1X_CLASS_GR3D = 0x60,
 };
 
-- 
1.8.1.5


  parent reply	other threads:[~2015-05-21 13:20 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-21 13:20 [PATCH 0/4] Add VIC support for Tegra124 Arto Merilainen
2015-05-21 13:20 ` Arto Merilainen
2015-05-21 13:20 ` [PATCH 1/4] host1x: Store device address to all bufs Arto Merilainen
2015-05-21 13:20   ` Arto Merilainen
2015-05-21 13:20 ` [PATCH 2/4] host1x: Pass register value in firewall Arto Merilainen
2015-05-21 13:20   ` Arto Merilainen
2015-05-21 13:20 ` Arto Merilainen [this message]
2015-05-21 13:20   ` [PATCH 3/4] drm/tegra: Add VIC support Arto Merilainen
     [not found]   ` <1432214425-27137-4-git-send-email-amerilainen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-05-21 14:40     ` Mikko Perttunen
2015-05-21 14:40       ` Mikko Perttunen
     [not found]       ` <555DEE5F.2060100-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-05-21 15:10         ` Arto Merilainen
2015-05-21 15:10           ` Arto Merilainen
     [not found]           ` <555DF576.7070307-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2015-05-21 15:44             ` Mikko Perttunen
2015-05-21 15:44               ` Mikko Perttunen
     [not found]               ` <555DFD48.4000609-/1wQRMveznE@public.gmane.org>
2015-05-22 10:02                 ` Thierry Reding
2015-05-22 10:02                   ` Thierry Reding
2015-05-22 10:12                   ` Arto Merilainen
2015-05-22 10:12                     ` Arto Merilainen
2015-05-22 10:13                   ` Arto Merilainen
2015-05-22 10:13                     ` Arto Merilainen
2015-05-22 10:25       ` Thierry Reding
2015-05-22 10:25         ` Thierry Reding
2015-05-25  7:11         ` Arto Merilainen
2015-05-25  7:11           ` Arto Merilainen
2015-05-22 11:47   ` Thierry Reding
2015-05-22 11:47     ` Thierry Reding
2015-05-25  8:25     ` Arto Merilainen
2015-05-25  8:25       ` Arto Merilainen
2015-05-21 13:20 ` [PATCH 4/4] ARM: tegra: Add VIC for Tegra124 Arto Merilainen
2015-05-21 13:20   ` Arto Merilainen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1432214425-27137-4-git-send-email-amerilainen@nvidia.com \
    --to=amerilainen@nvidia.com \
    --cc=achew@nvidia.com \
    --cc=dnibade@nvidia.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=srasal@nvidia.com \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.