/*
* Tegra flcn common driver
*
* Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include /* for kzalloc */
#include /* for parsing ucode image wrt endianness */
#include /* for udelay */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dev.h"
#include "class_ids.h"
#include "bus_client.h"
#include "nvhost_acm.h"
#include "nvhost_vm.h"
#include "nvhost_scale.h"
#include "nvhost_channel.h"
#include "flcn.h"
#include "hw_flcn.h"
#include "t124/hardware_t124.h" /* for nvhost opcodes*/
#include "t124/t124.h"
#include "t210/t210.h"
#ifdef CONFIG_ARCH_TEGRA_18x_SOC
#include "t186/t186.h"
#endif
#ifdef CONFIG_TEGRA_T19X_GRHOST
#include "t194/t194.h"
#endif
static int nvhost_flcn_init_sw(struct platform_device *dev);
static int nvhost_flcn_deinit_sw(struct platform_device *dev);
#define FLCN_IDLE_TIMEOUT_DEFAULT 100000 /* 100 milliseconds */
#define FLCN_IDLE_CHECK_PERIOD 10 /* 10 usec */
static irqreturn_t flcn_isr(int irq, void *dev_id)
{
struct platform_device *pdev = (struct platform_device *)(dev_id);
struct nvhost_device_data *pdata = nvhost_get_devdata(pdev);
unsigned long flags;
spin_lock_irqsave(&pdata->mirq_lock, flags);
if (pdata->flcn_isr)
pdata->flcn_isr(pdev);
spin_unlock_irqrestore(&pdata->mirq_lock, flags);
return IRQ_HANDLED;
}
int flcn_intr_init(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = nvhost_get_devdata(pdev);
const char *dev_name;
int ret = 0;
if (!pdata->module_irq)
return 0;
pdata->irq = platform_get_irq(pdev, 0);
if (pdata->irq < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
return -ENXIO;
}
spin_lock_init(&pdata->mirq_lock);
dev_name = get_device_name_for_dev(pdev);
ret = request_irq(pdata->irq,
flcn_isr, 0,
dev_name, pdev);
if (ret) {
dev_err(&pdev->dev, "failed to request irq. err %d\n", ret);
return ret;
}
/* keep irq disabled */
disable_irq(pdata->irq);
return 0;
}
static int nvhost_flcn_wait_idle(struct platform_device *pdev)
{
int ret;
u32 val = 0;
void __iomem *addr = get_aperture(pdev, 0) + flcn_idlestate_r();
nvhost_dbg_fn("");
ret = readl_poll_timeout(addr, val, (val == 0), FLCN_IDLE_CHECK_PERIOD,
FLCN_IDLE_TIMEOUT_DEFAULT);
if (ret)
nvhost_err(&pdev->dev, "flcn_idle_state_r =%x\n", val);
else
nvhost_dbg_fn("done");
return ret;
}
static int nvhost_flcn_dma_wait_idle(struct platform_device *pdev)
{
int ret;
void __iomem *addr = get_aperture(pdev, 0) + flcn_dmatrfcmd_r();
u32 val;
nvhost_dbg_fn("");
ret = readl_poll_timeout(addr, val,
(flcn_dmatrfcmd_idle_v(val) ==
flcn_dmatrfcmd_idle_true_v()),
FLCN_IDLE_CHECK_PERIOD,
FLCN_IDLE_TIMEOUT_DEFAULT);
if (ret)
nvhost_err(&pdev->dev, "flcn_idle_state_r =%x\n", val);
else
nvhost_dbg_fn("done");
return ret;
}
static int flcn_dma_pa_to_internal_256b(struct platform_device *pdev,
phys_addr_t pa, u32 internal_offset,
bool imem)
{
struct nvhost_device_data *pdata = nvhost_get_devdata(pdev);
u32 cmd = flcn_dmatrfcmd_size_256b_f();
u32 pa_offset = flcn_dmatrffboffs_offs_f(pa);
u32 i_offset = flcn_dmatrfmoffs_offs_f(internal_offset);
if (imem)
cmd |= flcn_dmatrfcmd_imem_true_f();
if (pdata->isolate_contexts)
cmd |= flcn_dmatrfcmd_dmactx_f(1);
host1x_writel(pdev, flcn_dmatrfmoffs_r(), i_offset);
host1x_writel(pdev, flcn_dmatrffboffs_r(), pa_offset);
host1x_writel(pdev, flcn_dmatrfcmd_r(), cmd);
return nvhost_flcn_dma_wait_idle(pdev);
}
int nvhost_flcn_load_image(struct platform_device *pdev,
dma_addr_t dma_addr,
struct flcn_os_image *os,
u32 imem_offset)
{
int ret = 0;
u32 offset;
host1x_writel(pdev, flcn_dmactl_r(), 0);
host1x_writel(pdev, flcn_dmatrfbase_r(),
(dma_addr + os->bin_data_offset) >> 8);
/* Write ucode data to dmem */
dev_dbg(&pdev->dev, "flcn_boot: load dmem\n");
for (offset = 0; offset < os->data_size; offset += 256) {
ret = flcn_dma_pa_to_internal_256b(pdev,
os->data_offset + offset,
offset, false);
if (ret)
goto err;
}
/* Write nvdec ucode to imem */
dev_dbg(&pdev->dev, "flcn_boot: load imem\n");
for (offset = imem_offset; offset < os->code_size; offset += 256) {
ret = flcn_dma_pa_to_internal_256b(pdev, os->code_offset + offset,
offset, true);
if (ret)
goto err;
}
err:
if (ret)
nvhost_err(&pdev->dev, "flcn_load_image failed: 0x%x\n", ret);
return ret;
}
static int flcn_setup_ucode_fce(struct platform_device *dev,
struct flcn *v,
struct ucode_v1_flcn *ucode)
{
u32 *ucode_ptr = v->mapped;
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
if (ucode->bin_header->fce_bin_header_offset == 0xa5a5a5a5)
return 0;
ucode->fce_header = (struct ucode_fce_header_v1_flcn *)
(((void *)ucode_ptr) +
ucode->bin_header->fce_bin_header_offset);
nvhost_dbg_info("fce ucode header: offset, buffer_size, size: 0x%x 0x%x 0x%x",
ucode->fce_header->fce_ucode_offset,
ucode->fce_header->fce_ucode_buffer_size,
ucode->fce_header->fce_ucode_size);
/* if isolation is enabled.. */
if (pdata->isolate_contexts) {
/* create and map fce shadow to all contexts */
v->fce_mapped = nvhost_vm_allocate_firmware_area(dev,
ucode->fce_header->fce_ucode_size,
&v->fce_dma_addr);
if (!v->fce_mapped)
return -ENOMEM;
memcpy(v->fce_mapped, (u8 *)v->mapped +
ucode->bin_header->fce_bin_data_offset,
ucode->fce_header->fce_ucode_size);
} else {
/* ..otherwise use the one in firmware image */
v->fce_dma_addr = v->dma_addr +
ucode->bin_header->fce_bin_data_offset;
}
v->fce.size = ucode->fce_header->fce_ucode_size;
v->fce.data_offset = ucode->bin_header->fce_bin_data_offset;
return 0;
}
int flcn_setup_ucode_image(struct platform_device *dev,
struct flcn *v,
const struct firmware *ucode_fw,
struct ucode_v1_flcn *ucode)
{
int w;
u32 *ucode_ptr = v->mapped;
nvhost_dbg_fn("");
/* image data is little endian. */
/* copy the whole thing taking into account endianness */
for (w = 0; w < ucode_fw->size/sizeof(u32); w++)
ucode_ptr[w] = le32_to_cpu(((__le32 *)ucode_fw->data)[w]);
ucode->bin_header = (struct ucode_bin_header_v1_flcn *)ucode_ptr;
/* endian problems would show up right here */
if (ucode->bin_header->bin_magic != 0x10de &&
ucode->bin_header->bin_magic != 0x10fe) {
dev_err(&dev->dev, "failed to get firmware magic");
return -EINVAL;
}
if (ucode->bin_header->bin_ver != 1) {
dev_err(&dev->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(&dev->dev, "ucode image size inconsistency");
return -EINVAL;
}
nvhost_dbg_info("ucode bin header: magic:0x%x ver:%d size:%d",
ucode->bin_header->bin_magic,
ucode->bin_header->bin_ver,
ucode->bin_header->bin_size);
nvhost_dbg_info("ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d",
ucode->bin_header->os_bin_header_offset,
ucode->bin_header->os_bin_data_offset,
ucode->bin_header->os_bin_size);
nvhost_dbg_info("ucode bin header: fce bin (header,data) offset size: 0x%x, 0x%x %d",
ucode->bin_header->fce_bin_header_offset,
ucode->bin_header->fce_bin_data_offset,
ucode->bin_header->fce_bin_size);
ucode->os_header = (struct ucode_os_header_v1_flcn *)
(((void *)ucode_ptr) + ucode->bin_header->os_bin_header_offset);
nvhost_dbg_info("os ucode header: os code (offset,size): 0x%x, 0x%x",
ucode->os_header->os_code_offset,
ucode->os_header->os_code_size);
nvhost_dbg_info("os ucode header: os data (offset,size): 0x%x, 0x%x",
ucode->os_header->os_data_offset,
ucode->os_header->os_data_size);
nvhost_dbg_info("os ucode header: num apps: %d", ucode->os_header->num_apps);
v->os.size = ucode->bin_header->os_bin_size;
v->os.bin_data_offset = ucode->bin_header->os_bin_data_offset;
v->os.code_offset = ucode->os_header->os_code_offset;
v->os.data_offset = ucode->os_header->os_data_offset;
v->os.data_size = ucode->os_header->os_data_size;
v->os.code_size = ucode->os_header->os_code_size;
v->os.bin_magic = ucode->bin_header->bin_magic;
v->os.bin_ver_tag = ucode->bin_header->bin_ver_tag;
return 0;
}
int flcn_reload_fw(struct platform_device *pdev)
{
int err;
struct device *device = &pdev->dev;
err = nvhost_module_do_idle(device);
if (err)
return err;
nvhost_flcn_deinit_sw(pdev);
err = nvhost_module_do_unidle(device);
if (err)
return err;
return 0;
}
static int flcn_read_ucode(struct platform_device *dev,
const char *fw_name,
struct flcn *v)
{
const struct firmware *ucode_fw;
struct ucode_v1_flcn ucode;
int err;
DEFINE_DMA_ATTRS(attrs);
nvhost_dbg_fn("");
dma_set_attr(DMA_ATTR_READ_ONLY, __DMA_ATTR(attrs));
v->dma_addr = 0;
v->mapped = NULL;
ucode_fw = nvhost_client_request_firmware(dev, fw_name);
if (!ucode_fw) {
nvhost_dbg_fn("request firmware failed");
dev_err(&dev->dev, "failed to get firmware\n");
err = -ENOENT;
return err;
}
v->size = ucode_fw->size;
v->mapped = dma_alloc_attrs(&dev->dev, v->size, &v->dma_addr,
GFP_KERNEL, __DMA_ATTR(attrs));
if (!v->mapped) {
dev_err(&dev->dev, "dma memory allocation failed");
err = -ENOMEM;
goto clean_up;
}
err = flcn_setup_ucode_image(dev, v, ucode_fw, &ucode);
if (err) {
dev_err(&dev->dev, "failed to parse firmware image\n");
goto clean_up;
}
err = flcn_setup_ucode_fce(dev, v, &ucode);
if (err) {
dev_err(&dev->dev, "failed to parse fce image\n");
goto clean_up;
}
v->valid = true;
release_firmware(ucode_fw);
return 0;
clean_up:
if (v->mapped) {
dma_free_attrs(&dev->dev, v->size, v->mapped, v->dma_addr,
__DMA_ATTR(attrs));
v->mapped = NULL;
v->dma_addr = 0;
}
release_firmware(ucode_fw);
return err;
}
int nvhost_flcn_wait_mem_scrubbing(struct platform_device *dev)
{
int retries = FLCN_IDLE_TIMEOUT_DEFAULT / FLCN_IDLE_CHECK_PERIOD;
nvhost_dbg_fn("");
do {
u32 w = host1x_readl(dev, flcn_dmactl_r()) &
(flcn_dmactl_dmem_scrubbing_m() |
flcn_dmactl_imem_scrubbing_m());
if (!w) {
nvhost_dbg_fn("done");
return 0;
}
udelay(FLCN_IDLE_CHECK_PERIOD);
} while (--retries || !tegra_platform_is_silicon());
nvhost_err(&dev->dev, "Falcon mem scrubbing timeout");
return -ETIMEDOUT;
}
int nvhost_flcn_prepare_poweroff(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
if (pdata->flcn_isr)
disable_irq(pdata->irq);
return 0;
}
void nvhost_flcn_irq_mask_set(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
/* setup falcon interrupts and enable interface */
if (!pdata->self_config_flcn_isr)
host1x_writel(pdev, flcn_irqmset_r(),
(flcn_irqmset_ext_f(0xff) |
flcn_irqmset_swgen1_set_f() |
flcn_irqmset_swgen0_set_f() |
flcn_irqmset_exterr_set_f() |
flcn_irqmset_halt_set_f() |
flcn_irqmset_wdtmr_set_f()));
}
void nvhost_flcn_irq_dest_set(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
if (!pdata->self_config_flcn_isr)
host1x_writel(pdev, flcn_irqdest_r(),
(flcn_irqdest_host_ext_f(0xff) |
flcn_irqdest_host_swgen1_host_f() |
flcn_irqdest_host_swgen0_host_f() |
flcn_irqdest_host_exterr_host_f() |
flcn_irqdest_host_halt_host_f()));
}
void nvhost_flcn_ctxtsw_init(struct platform_device *pdev)
{
host1x_writel(pdev, flcn_itfen_r(),
(flcn_itfen_mthden_enable_f() |
flcn_itfen_ctxen_enable_f()));
}
int nvhost_flcn_start(struct platform_device *pdev, u32 bootvec)
{
int err = 0;
/* boot falcon */
dev_dbg(&pdev->dev, "flcn_boot: start falcon\n");
host1x_writel(pdev, flcn_bootvec_r(), flcn_bootvec_vec_f(bootvec));
host1x_writel(pdev, flcn_cpuctl_r(), flcn_cpuctl_startcpu_true_f());
err = nvhost_flcn_wait_idle(pdev);
if (err != 0)
nvhost_err(&pdev->dev, "boot failed due to timeout");
return err;
}
int nvhost_flcn_finalize_poweron(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct flcn *v;
int err = 0;
err = nvhost_flcn_init_sw(pdev);
if (err)
return err;
v = get_flcn(pdev);
err = nvhost_flcn_wait_mem_scrubbing(pdev);
if (err)
return err;
/* load transcfg configuration if defined */
if (pdata->transcfg_addr)
host1x_writel(pdev, pdata->transcfg_addr, pdata->transcfg_val);
err = nvhost_flcn_load_image(pdev, v->dma_addr, &v->os, 0);
if (err)
return err;
nvhost_flcn_irq_mask_set(pdev);
nvhost_flcn_irq_dest_set(pdev);
if (pdata->flcn_isr)
enable_irq(pdata->irq);
nvhost_flcn_ctxtsw_init(pdev);
err = nvhost_flcn_start(pdev, 0);
return err;
}
int nvhost_flcn_common_isr(struct platform_device *pdev)
{
u32 irqstat, exci, mailbox0, mailbox1;
irqstat = host1x_readl(pdev, flcn_irqstat_r());
exci = host1x_readl(pdev, flcn_exci_r());
mailbox0 = host1x_readl(pdev, flcn_mailbox0_r());
mailbox1 = host1x_readl(pdev, flcn_mailbox1_r());
dev_err(&pdev->dev, "irqstat: %08x, exci: %08x, mailbox0: %08x, mailbox1: %08x",
irqstat, exci, mailbox0, mailbox1);
/* logic to clear the interrupt */
host1x_writel(pdev, flcn_thi_int_stat_r(), flcn_thi_int_stat_clr_f());
host1x_readl(pdev, flcn_thi_int_stat_r());
host1x_writel(pdev, flcn_irqsclr_r(), flcn_irqsclr_swgen0_set_f());
return 0;
}
static int nvhost_flcn_init_sw(struct platform_device *dev)
{
int err = 0;
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
struct flcn *v = get_flcn(dev);
nvhost_dbg_fn("in dev:%p v:%p", dev, v);
if (v)
return 0;
v = kzalloc(sizeof(*v), GFP_KERNEL);
if (!v) {
err = -ENOMEM;
goto clean_up;
}
set_flcn(dev, v);
nvhost_dbg_fn("primed dev:%p v:%p", dev, v);
err = flcn_read_ucode(dev, pdata->firmware_name, v);
if (err || !v->valid)
goto clean_up;
return 0;
clean_up:
nvhost_err(&dev->dev, "failed : 0x%x", err);
return err;
}
static int nvhost_flcn_deinit_sw(struct platform_device *dev)
{
struct flcn *v = get_flcn(dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
DEFINE_DMA_ATTRS(attrs);
dma_set_attr(DMA_ATTR_READ_ONLY, &attrs);
#endif
if (!v)
return 0;
if (v->mapped) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
dma_free_attrs(&dev->dev, v->size, v->mapped, v->dma_addr,
&attrs);
#else
dma_free_attrs(&dev->dev, v->size, v->mapped, v->dma_addr,
DMA_ATTR_READ_ONLY);
#endif
v->mapped = NULL;
v->dma_addr = 0;
}
kfree(v);
set_flcn(dev, NULL);
return 0;
}
int nvhost_vic_finalize_poweron(struct platform_device *pdev)
{
struct flcn *v;
int err;
err = nvhost_flcn_finalize_poweron(pdev);
if (err)
return err;
v = get_flcn(pdev);
host1x_writel(pdev, FLCN_UCLASS_METHOD_OFFSET * 4,
NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID >> 2);
host1x_writel(pdev, FLCN_UCLASS_METHOD_DATA * 4, 1);
host1x_writel(pdev, FLCN_UCLASS_METHOD_OFFSET * 4,
NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE >> 2);
host1x_writel(pdev, FLCN_UCLASS_METHOD_DATA * 4, v->fce.size);
host1x_writel(pdev, FLCN_UCLASS_METHOD_OFFSET * 4,
NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET >> 2);
host1x_writel(pdev, FLCN_UCLASS_METHOD_DATA * 4,
(v->dma_addr + v->fce.data_offset) >> 8);
return 0;
}
int nvhost_vic_init_context(struct platform_device *pdev,
struct nvhost_cdma *cdma)
{
struct flcn *v;
int err;
err = nvhost_flcn_init_sw(pdev);
if (err)
return err;
v = get_flcn(pdev);
/* load application id */
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_OFFSET, 1),
NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID >> 2);
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_DATA, 1), 1);
/* set fce ucode size */
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_OFFSET, 1),
NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE >> 2);
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_DATA, 1), v->fce.size);
/* set fce ucode offset */
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_OFFSET, 1),
NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET >> 2);
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID,
FLCN_UCLASS_METHOD_DATA, 1),
v->fce_dma_addr >> 8);
return 0;
}
void flcn_enable_timestamps(struct platform_device *pdev,
struct nvhost_cdma *cdma,
dma_addr_t timestamp_addr)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
/* set timestamp buffer offset */
nvhost_cdma_push(cdma, nvhost_opcode_setclass(pdata->class,
FLCN_UCLASS_METHOD_OFFSET, 1),
FLCN_UCLASS_METHOD_ADDR_TSP);
nvhost_cdma_push(cdma, nvhost_opcode_setclass(pdata->class,
FLCN_UCLASS_METHOD_DATA, 1),
timestamp_addr >> 8);
}
int nvhost_vic_aggregate_constraints(struct platform_device *dev,
int clk_index,
unsigned long floor_rate,
unsigned long pixelrate,
unsigned long bw_constraint)
{
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
if (!pdata) {
dev_err(&dev->dev, "no platform data\n");
/* return 0 to fall-back on default policy */
return 0;
}
/* Fall-back to default policy if pixelrate
* is unavailable or clk index is incorrect.
* Here clk_index 2 is for floor client.
*/
if (!pixelrate || clk_index != 2)
return 0;
/* Compute VIC frequency based on pixelrate */
return pixelrate / pdata->num_ppc;
}
static struct of_device_id tegra_flcn_of_match[] = {
#ifdef CONFIG_TEGRA_GRHOST_VIC
{ .compatible = "nvidia,tegra124-vic",
.data = (struct nvhost_device_data *)&t124_vic_info },
{ .compatible = "nvidia,tegra210-vic",
.data = (struct nvhost_device_data *)&t21_vic_info },
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{ .compatible = "nvidia,tegra124-msenc",
.data = (struct nvhost_device_data *)&t124_msenc_info },
#endif
#ifdef TEGRA_21X_OR_HIGHER_CONFIG
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{ .compatible = "nvidia,tegra210-nvenc",
.data = (struct nvhost_device_data *)&t21_msenc_info },
#endif
#endif
#ifdef TEGRA_21X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra210-nvjpg",
.data = (struct nvhost_device_data *)&t21_nvjpg_info },
#endif
#ifdef CONFIG_ARCH_TEGRA_18x_SOC
#if defined(CONFIG_TEGRA_GRHOST_VIC)
{ .compatible = "nvidia,tegra186-vic",
.data = (struct nvhost_device_data *)&t18_vic_info },
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVJPG)
{ .compatible = "nvidia,tegra186-nvjpg",
.data = (struct nvhost_device_data *)&t18_nvjpg_info },
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{ .compatible = "nvidia,tegra186-nvenc",
.data = (struct nvhost_device_data *)&t18_msenc_info },
#endif
#endif
#ifdef CONFIG_TEGRA_T19X_GRHOST
#if defined(CONFIG_TEGRA_GRHOST_VIC)
{ .compatible = "nvidia,tegra194-vic",
.data = (struct nvhost_device_data *)&t19_vic_info },
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVJPG)
{ .compatible = "nvidia,tegra194-nvjpg",
.data = (struct nvhost_device_data *)&t19_nvjpg_info },
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{ .compatible = "nvidia,tegra194-nvenc",
.data = (struct nvhost_device_data *)&t19_msenc_info,
.name = "nvenc" },
{ .compatible = "nvidia,tegra194-nvenc",
.data = (struct nvhost_device_data *)&t19_nvenc1_info,
.name = "nvenc1" },
#endif
#endif
{ },
};
static ssize_t reload_fw_write(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long val = 0;
struct platform_device *pdev = to_platform_device(device);
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
if (!val)
return -EINVAL;
err = flcn_reload_fw(pdev);
if (err)
return err;
return count;
}
static DEVICE_ATTR(reload_fw, 0200, NULL, reload_fw_write);
static int flcn_probe(struct platform_device *dev)
{
int err;
struct nvhost_device_data *pdata = NULL;
if (dev->dev.of_node) {
const struct of_device_id *match;
match = of_match_device(tegra_flcn_of_match, &dev->dev);
if (match)
pdata = (struct nvhost_device_data *)match->data;
} else
pdata = (struct nvhost_device_data *)dev->dev.platform_data;
if (!pdata) {
dev_err(&dev->dev, "no platform data\n");
return -ENODATA;
}
nvhost_dbg_fn("dev:%p pdata:%p", dev, pdata);
pdata->pdev = dev;
mutex_init(&pdata->lock);
platform_set_drvdata(dev, pdata);
err = device_create_file(&dev->dev, &dev_attr_reload_fw);
if (err)
return err;
err = nvhost_client_device_get_resources(dev);
if (err)
return err;
dev->dev.platform_data = NULL;
nvhost_module_init(dev);
err = nvhost_client_device_init(dev);
if (err) {
nvhost_dbg_fn("failed to init client device for %s",
dev->name);
pm_runtime_put(&dev->dev);
return err;
}
if (pdata->flcn_isr)
flcn_intr_init(dev);
return 0;
}
static int __exit flcn_remove(struct platform_device *pdev)
{
nvhost_client_device_release(pdev);
return 0;
}
static struct platform_device_id flcn_id_table[] = {
{ .name = "vic03" },
{ .name = "msenc" },
{ .name = "msenc" },
{ .name = "nvjpg" },
{},
};
static struct platform_driver flcn_driver = {
.probe = flcn_probe,
.remove = __exit_p(flcn_remove),
.driver = {
.owner = THIS_MODULE,
.name = "falcon",
#ifdef CONFIG_OF
.of_match_table = tegra_flcn_of_match,
#endif
#ifdef CONFIG_PM
.pm = &nvhost_module_pm_ops,
#endif
.suppress_bind_attrs = true,
},
.id_table = flcn_id_table,
};
static struct of_device_id tegra_flcn_domain_match[] = {
#ifdef CONFIG_TEGRA_GRHOST_VIC
{.compatible = "nvidia,tegra124-vic03-pd",
.data = (struct nvhost_device_data *)&t124_vic_info},
{.compatible = "nvidia,tegra132-vic03-pd",
.data = (struct nvhost_device_data *)&t124_vic_info},
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{.compatible = "nvidia,tegra124-msenc-pd",
.data = (struct nvhost_device_data *)&t124_msenc_info},
{.compatible = "nvidia,tegra132-msenc-pd",
.data = (struct nvhost_device_data *)&t124_msenc_info},
#endif
#ifdef CONFIG_TEGRA_GRHOST_VIC
{.compatible = "nvidia,tegra210-vic03-pd",
.data = (struct nvhost_device_data *)&t21_vic_info},
#endif
#ifdef TEGRA_21X_OR_HIGHER_CONFIG
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{.compatible = "nvidia,tegra210-msenc-pd",
.data = (struct nvhost_device_data *)&t21_msenc_info},
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVJPG)
{.compatible = "nvidia,tegra210-nvjpg-pd",
.data = (struct nvhost_device_data *)&t21_nvjpg_info},
#endif
#endif
#ifdef CONFIG_ARCH_TEGRA_18x_SOC
#if defined(CONFIG_TEGRA_GRHOST_VIC)
{.compatible = "nvidia,tegra186-vic03-pd",
.data = (struct nvhost_device_data *)&t18_vic_info},
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{.compatible = "nvidia,tegra186-msenc-pd",
.data = (struct nvhost_device_data *)&t18_msenc_info},
#endif
#if defined(CONFIG_TEGRA_GRHOST_NVJPG)
{.compatible = "nvidia,tegra186-nvjpg-pd",
.data = (struct nvhost_device_data *)&t18_nvjpg_info},
#endif
#endif
#ifdef CONFIG_TEGRA_T19X_GRHOST
#if defined(CONFIG_TEGRA_GRHOST_NVENC)
{.compatible = "nvidia,tegra194-nvenc1-pd",
.data = (struct nvhost_device_data *)&t19_nvenc1_info},
#endif
#endif
{},
};
static int __init flcn_init(void)
{
int ret;
ret = nvhost_domain_init(tegra_flcn_domain_match);
if (ret)
return ret;
return platform_driver_register(&flcn_driver);
}
static void __exit flcn_exit(void)
{
platform_driver_unregister(&flcn_driver);
}
module_init(flcn_init);
module_exit(flcn_exit);