Thank you Mikko for your comments! Please see my answers inline.
- Arto On 05/21/2015 05:40 PM, Mikko Perttunen wrote: > Hi, very good patch! > > Here are a few small comments. Aside those, you should also add a > section to > Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt in a > separate patch. Will do. > On 05/21/2015 04:20 PM, Arto Merilainen wrote: >> 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 at nvidia.com> >> Signed-off-by: Arto Merilainen <amerilainen at 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; > > This doesn't seem to be needed here. > >> + >> +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); > > Should this not be freed with tegra_bo_free or similar? Right now this > looks like it would leak at least memory. > Uh, this definitely looks broken. Will fix. >> +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); > > Same here, is this the correct way to free this? > Same as above. >> + 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; > > "return (offset == 0x2b);" perhaps? > Works for me. >> + } >> + >> + /* 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; >> + } > > This doesn't seem necessary, we can only be probed from device tree and > each match entry has a data pointer anyway. > True, will remove. >> + >> + 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"); > > I might prefer just "vic" as the clock/reset name. The name is often > used as a sort of "role" for the clock/reset for the device, not > necessarily the raw name of the "correct" clock/reset. > I considered that - but I then noticed that drivers/clk/tegra/clk-tegra124.c was already using vic03 variant. I can write a patch for changing that too. >> + 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) { > > You used 'if (err) {' previously, so maybe also here. > True, will fix. >> + 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) { > > and here. > Will fix. >> + 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, >> }; >> >>