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
next prev 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: linkBe 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.