703 lines
17 KiB
C
703 lines
17 KiB
C
/*
|
|
* drivers/video/tegra/host/vic/vic03.c
|
|
*
|
|
* Tegra VIC03 Module Support
|
|
*
|
|
* Copyright (c) 2011-2013, 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/slab.h> /* for kzalloc */
|
|
#include <asm/byteorder.h> /* for parsing ucode image wrt endianness */
|
|
#include <linux/delay.h> /* for udelay */
|
|
#include <linux/export.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/nvmap.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/tegra-powergate.h>
|
|
#include <linux/tegra-soc.h>
|
|
|
|
#include "dev.h"
|
|
#include "class_ids.h"
|
|
#include "bus_client.h"
|
|
#include "nvhost_as.h"
|
|
#include "nvhost_acm.h"
|
|
#include "nvhost_scale.h"
|
|
|
|
#include "host1x/host1x_hwctx.h"
|
|
|
|
#include "vic03.h"
|
|
#include "hw_flcn_vic03.h"
|
|
#include "hw_tfbif_vic03.h"
|
|
|
|
#include "t124/hardware_t124.h" /* for nvhost opcodes*/
|
|
#include "t124/t124.h"
|
|
|
|
#include <linux/platform_data/tegra_pm_domains.h>
|
|
|
|
#include "../../../../../arch/arm/mach-tegra/iomap.h"
|
|
|
|
static inline struct vic03 *get_vic03(struct platform_device *dev)
|
|
{
|
|
return (struct vic03 *)nvhost_get_private_data(dev);
|
|
}
|
|
static inline void set_vic03(struct platform_device *dev, struct vic03 *vic03)
|
|
{
|
|
nvhost_set_private_data(dev, vic03);
|
|
}
|
|
|
|
/* caller is responsible for freeing */
|
|
static char *vic_get_fw_name(struct platform_device *dev)
|
|
{
|
|
char *fw_name;
|
|
u8 maj, min;
|
|
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
|
|
|
|
/* note size here is a little over...*/
|
|
fw_name = kzalloc(32, GFP_KERNEL);
|
|
if (!fw_name)
|
|
return NULL;
|
|
|
|
decode_vic_ver(pdata->version, &maj, &min);
|
|
sprintf(fw_name, "vic%02d_ucode.bin", maj);
|
|
dev_info(&dev->dev, "fw name:%s\n", fw_name);
|
|
|
|
return fw_name;
|
|
}
|
|
|
|
#define VIC_IDLE_TIMEOUT_DEFAULT 10000 /* 10 milliseconds */
|
|
#define VIC_IDLE_CHECK_PERIOD 10 /* 10 usec */
|
|
static int vic03_flcn_wait_idle(struct platform_device *dev,
|
|
u32 *timeout)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
|
|
nvhost_dbg_fn("");
|
|
|
|
if (!*timeout)
|
|
*timeout = VIC_IDLE_TIMEOUT_DEFAULT;
|
|
|
|
do {
|
|
u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, *timeout);
|
|
u32 w = vic03_readl(v, flcn_idlestate_r());
|
|
|
|
if (!w) {
|
|
nvhost_dbg_fn("done");
|
|
return 0;
|
|
}
|
|
udelay(VIC_IDLE_CHECK_PERIOD);
|
|
*timeout -= check;
|
|
} while (*timeout);
|
|
|
|
dev_err(&dev->dev, "vic03 flcn idle timeout");
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int vic03_flcn_dma_wait_idle(struct platform_device *dev, u32 *timeout)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
nvhost_dbg_fn("");
|
|
|
|
if (!*timeout)
|
|
*timeout = VIC_IDLE_TIMEOUT_DEFAULT;
|
|
|
|
do {
|
|
u32 check = min_t(u32, VIC_IDLE_CHECK_PERIOD, *timeout);
|
|
u32 dmatrfcmd = vic03_readl(v, flcn_dmatrfcmd_r());
|
|
u32 idle_v = flcn_dmatrfcmd_idle_v(dmatrfcmd);
|
|
|
|
if (flcn_dmatrfcmd_idle_true_v() == idle_v) {
|
|
nvhost_dbg_fn("done");
|
|
return 0;
|
|
}
|
|
udelay(VIC_IDLE_CHECK_PERIOD);
|
|
*timeout -= check;
|
|
} while (*timeout);
|
|
|
|
dev_err(&dev->dev, "vic03 dma idle timeout");
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int vic03_flcn_dma_pa_to_internal_256b(struct platform_device *dev,
|
|
phys_addr_t pa,
|
|
u32 internal_offset,
|
|
bool imem)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
|
|
u32 cmd = flcn_dmatrfcmd_size_256b_f();
|
|
u32 pa_offset = flcn_dmatrffboffs_offs_f(pa);
|
|
u32 i_offset = flcn_dmatrfmoffs_offs_f(internal_offset);
|
|
u32 timeout = 0; /* default*/
|
|
|
|
if (imem)
|
|
cmd |= flcn_dmatrfcmd_imem_true_f();
|
|
|
|
vic03_writel(v, flcn_dmatrfmoffs_r(), i_offset);
|
|
vic03_writel(v, flcn_dmatrffboffs_r(), pa_offset);
|
|
vic03_writel(v, flcn_dmatrfcmd_r(), cmd);
|
|
|
|
return vic03_flcn_dma_wait_idle(dev, &timeout);
|
|
|
|
}
|
|
|
|
static int vic03_setup_ucode_image(struct platform_device *dev,
|
|
u32 *ucode_ptr,
|
|
const struct firmware *ucode_fw)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
/* image data is little endian. */
|
|
struct ucode_v1_vic03 ucode;
|
|
int w;
|
|
|
|
/* copy the whole thing taking into account endianness */
|
|
for (w = 0; w < ucode_fw->size/sizeof(u32); w++)
|
|
ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
|
|
|
|
ucode.bin_header = (struct ucode_bin_header_v1_vic03 *)ucode_ptr;
|
|
/* endian problems would show up right here */
|
|
if (ucode.bin_header->bin_magic != 0x10de) {
|
|
dev_err(&dev->dev,
|
|
"failed to get vic03 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("vic03 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("vic03 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("vic03 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_vic03 *)
|
|
(((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset);
|
|
|
|
nvhost_dbg_info("vic03 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("vic03 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("vic03 os ucode header: num apps: %d",
|
|
ucode.os_header->num_apps);
|
|
|
|
ucode.fce_header = (struct ucode_fce_header_v1_vic03 *)
|
|
(((void *)ucode_ptr) + ucode.bin_header->fce_bin_header_offset);
|
|
|
|
nvhost_dbg_info("vic03 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);
|
|
|
|
v->ucode.os.size = ucode.bin_header->os_bin_size;
|
|
v->ucode.os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
|
|
v->ucode.os.code_offset = ucode.os_header->os_code_offset;
|
|
v->ucode.os.data_offset = ucode.os_header->os_data_offset;
|
|
v->ucode.os.data_size = ucode.os_header->os_data_size;
|
|
|
|
v->ucode.fce.size = ucode.fce_header->fce_ucode_size;
|
|
v->ucode.fce.data_offset = ucode.bin_header->fce_bin_data_offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vic03_read_ucode(struct platform_device *dev, const char *fw_name)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
const struct firmware *ucode_fw;
|
|
int err;
|
|
DEFINE_DMA_ATTRS(attrs);
|
|
|
|
v->ucode.dma_addr = 0;
|
|
v->ucode.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 vic03 firmware\n");
|
|
err = -ENOENT;
|
|
return err;
|
|
}
|
|
|
|
v->ucode.size = ucode_fw->size;
|
|
|
|
v->ucode.mapped = dma_alloc_attrs(&dev->dev,
|
|
v->ucode.size, &v->ucode.dma_addr,
|
|
GFP_KERNEL, &attrs);
|
|
if (!v->ucode.mapped) {
|
|
dev_err(&dev->dev, "dma memory allocation failed");
|
|
err = -ENOMEM;
|
|
goto clean_up;
|
|
}
|
|
|
|
err = vic03_setup_ucode_image(dev, v->ucode.mapped, ucode_fw);
|
|
if (err) {
|
|
dev_err(&dev->dev, "failed to parse firmware image\n");
|
|
goto clean_up;
|
|
}
|
|
|
|
v->ucode.valid = true;
|
|
|
|
release_firmware(ucode_fw);
|
|
|
|
return 0;
|
|
|
|
clean_up:
|
|
if (v->ucode.mapped) {
|
|
dma_free_attrs(&dev->dev,
|
|
v->ucode.size, v->ucode.mapped,
|
|
v->ucode.dma_addr, &attrs);
|
|
v->ucode.mapped = NULL;
|
|
v->ucode.dma_addr = 0;
|
|
}
|
|
release_firmware(ucode_fw);
|
|
return err;
|
|
}
|
|
|
|
static int vic03_boot(struct platform_device *dev)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
u32 timeout;
|
|
u32 offset;
|
|
int err = 0;
|
|
|
|
/* check if firmware is loaded or not */
|
|
if (!v || !v->ucode.valid)
|
|
return -ENOMEDIUM;
|
|
|
|
if (v->is_booted)
|
|
return 0;
|
|
|
|
vic03_writel(v, flcn_dmactl_r(), 0);
|
|
|
|
vic03_writel(v, flcn_dmatrfbase_r(),
|
|
(v->ucode.dma_addr + v->ucode.os.bin_data_offset) >> 8);
|
|
|
|
for (offset = 0; offset < v->ucode.os.data_size; offset += 256)
|
|
vic03_flcn_dma_pa_to_internal_256b(dev,
|
|
v->ucode.os.data_offset + offset,
|
|
offset, false);
|
|
|
|
vic03_flcn_dma_pa_to_internal_256b(dev, v->ucode.os.code_offset,
|
|
0, true);
|
|
|
|
/* setup falcon interrupts and enable interface */
|
|
vic03_writel(v, 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()));
|
|
vic03_writel(v, 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()));
|
|
vic03_writel(v, flcn_itfen_r(), (flcn_itfen_mthden_enable_f() |
|
|
flcn_itfen_ctxen_enable_f()));
|
|
|
|
/* boot falcon */
|
|
vic03_writel(v, flcn_bootvec_r(), flcn_bootvec_vec_f(0));
|
|
vic03_writel(v, flcn_cpuctl_r(), flcn_cpuctl_startcpu_true_f());
|
|
|
|
timeout = 0; /* default */
|
|
|
|
err = vic03_flcn_wait_idle(dev, &timeout);
|
|
if (err != 0) {
|
|
dev_err(&dev->dev, "boot failed due to timeout");
|
|
return err;
|
|
}
|
|
|
|
v->is_booted = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nvhost_vic03_init(struct platform_device *dev)
|
|
{
|
|
int err = 0;
|
|
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
|
|
struct vic03 *v = get_vic03(dev);
|
|
char *fw_name;
|
|
|
|
nvhost_dbg_fn("in dev:%p v:%p", dev, v);
|
|
|
|
fw_name = vic_get_fw_name(dev);
|
|
if (!fw_name) {
|
|
dev_err(&dev->dev, "couldn't determine firmware name");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!v) {
|
|
nvhost_dbg_fn("allocating vic03 support");
|
|
v = kzalloc(sizeof(*v), GFP_KERNEL);
|
|
if (!v) {
|
|
dev_err(&dev->dev, "couldn't alloc vic03 support");
|
|
err = -ENOMEM;
|
|
goto clean_up;
|
|
}
|
|
set_vic03(dev, v);
|
|
v->is_booted = false;
|
|
}
|
|
nvhost_dbg_fn("primed dev:%p v:%p", dev, v);
|
|
|
|
v->host = nvhost_get_host(dev);
|
|
v->regs = pdata->aperture[0];
|
|
|
|
if (!v->ucode.valid)
|
|
err = vic03_read_ucode(dev, fw_name);
|
|
if (err)
|
|
goto clean_up;
|
|
|
|
kfree(fw_name);
|
|
fw_name = NULL;
|
|
|
|
nvhost_module_busy(dev);
|
|
err = vic03_boot(dev);
|
|
nvhost_module_idle(dev);
|
|
|
|
if (pdata->scaling_init)
|
|
nvhost_scale_hw_init(dev);
|
|
|
|
return 0;
|
|
|
|
clean_up:
|
|
kfree(fw_name);
|
|
nvhost_err(&dev->dev, "failed");
|
|
|
|
return err;
|
|
}
|
|
|
|
void nvhost_vic03_deinit(struct platform_device *dev)
|
|
{
|
|
struct vic03 *v = get_vic03(dev);
|
|
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
|
|
|
|
DEFINE_DMA_ATTRS(attrs);
|
|
|
|
if (!v)
|
|
return;
|
|
|
|
if (pdata->scaling_init)
|
|
nvhost_scale_hw_deinit(dev);
|
|
|
|
if (v->ucode.mapped) {
|
|
dma_free_attrs(&dev->dev,
|
|
v->ucode.size, v->ucode.mapped,
|
|
v->ucode.dma_addr, &attrs);
|
|
v->ucode.mapped = NULL;
|
|
v->ucode.dma_addr = 0;
|
|
}
|
|
|
|
if (v->regs)
|
|
v->regs = NULL;
|
|
|
|
/* zap, free */
|
|
set_vic03(dev, NULL);
|
|
kfree(v);
|
|
}
|
|
|
|
static struct nvhost_hwctx *vic03_alloc_hwctx(struct nvhost_hwctx_handler *h,
|
|
struct nvhost_channel *ch)
|
|
{
|
|
struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h);
|
|
|
|
struct vic03 *v = get_vic03(ch->dev);
|
|
struct host1x_hwctx *ctx;
|
|
u32 *ptr;
|
|
u32 syncpt = nvhost_get_devdata(ch->dev)->syncpts[0];
|
|
u32 nvhost_vic03_restore_size = 11; /* number of words written below */
|
|
|
|
nvhost_dbg_fn("");
|
|
|
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return NULL;
|
|
|
|
ctx->restore_size = nvhost_vic03_restore_size;
|
|
|
|
ctx->cpuva = dma_alloc_writecombine(&ch->dev->dev,
|
|
ctx->restore_size * 4,
|
|
&ctx->iova,
|
|
GFP_KERNEL);
|
|
if (!ctx->cpuva) {
|
|
dev_err(&ch->dev->dev, "memory allocation failed\n");
|
|
goto fail;
|
|
}
|
|
|
|
ptr = ctx->cpuva;
|
|
|
|
/* set class to vic */
|
|
ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_VIC_CLASS_ID, 0, 0);
|
|
|
|
/* set app id, fce ucode size, offset */
|
|
ptr[1] = nvhost_opcode_incr(VIC_UCLASS_METHOD_OFFSET, 2);
|
|
ptr[2] = NVA0B6_VIDEO_COMPOSITOR_SET_APPLICATION_ID >> 2;
|
|
ptr[3] = 1;
|
|
|
|
ptr[4] = nvhost_opcode_incr(VIC_UCLASS_METHOD_OFFSET, 2);
|
|
ptr[5] = NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_SIZE >> 2;
|
|
ptr[6] = v->ucode.fce.size;
|
|
|
|
ptr[7] = nvhost_opcode_incr(VIC_UCLASS_METHOD_OFFSET, 2);
|
|
ptr[8] = NVA0B6_VIDEO_COMPOSITOR_SET_FCE_UCODE_OFFSET >> 2;
|
|
ptr[9] = (v->ucode.dma_addr + v->ucode.fce.data_offset) >> 8;
|
|
|
|
/* syncpt increment to track restore gather. */
|
|
ptr[10] = nvhost_opcode_imm_incr_syncpt(
|
|
host1x_uclass_incr_syncpt_cond_op_done_v(),
|
|
syncpt);
|
|
|
|
kref_init(&ctx->hwctx.ref);
|
|
ctx->hwctx.h = &p->h;
|
|
ctx->hwctx.channel = ch;
|
|
ctx->hwctx.valid = true; /* this is a preconditioning sequence... */
|
|
ctx->hwctx.save_incrs = 0;
|
|
ctx->hwctx.save_slots = 0;
|
|
|
|
ctx->hwctx.restore_incrs = 1;
|
|
|
|
return &ctx->hwctx;
|
|
|
|
fail:
|
|
kfree(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
static void vic03_free_hwctx(struct kref *ref)
|
|
{
|
|
struct nvhost_hwctx *nctx = container_of(ref, struct nvhost_hwctx, ref);
|
|
struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
|
|
|
|
if (ctx->cpuva) {
|
|
dma_free_writecombine(&nctx->channel->dev->dev,
|
|
ctx->restore_size * 4,
|
|
ctx->cpuva,
|
|
ctx->iova);
|
|
ctx->cpuva = NULL;
|
|
ctx->iova = 0;
|
|
}
|
|
kfree(ctx);
|
|
}
|
|
|
|
static void vic03_get_hwctx(struct nvhost_hwctx *ctx)
|
|
{
|
|
nvhost_dbg_fn("");
|
|
kref_get(&ctx->ref);
|
|
}
|
|
static void vic03_put_hwctx(struct nvhost_hwctx *ctx)
|
|
{
|
|
nvhost_dbg_fn("");
|
|
kref_put(&ctx->ref, vic03_free_hwctx);
|
|
}
|
|
static void vic03_save_push_hwctx(struct nvhost_hwctx *ctx,
|
|
struct nvhost_cdma *cdma)
|
|
{
|
|
nvhost_dbg_fn("");
|
|
}
|
|
|
|
static void ctxvic03_restore_push(struct nvhost_hwctx *nctx,
|
|
struct nvhost_cdma *cdma)
|
|
{
|
|
struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
|
|
_nvhost_cdma_push_gather(cdma,
|
|
ctx->cpuva,
|
|
ctx->iova,
|
|
0,
|
|
nvhost_opcode_gather(ctx->restore_size),
|
|
ctx->iova);
|
|
}
|
|
|
|
struct nvhost_hwctx_handler *nvhost_vic03_alloc_hwctx_handler(u32 syncpt,
|
|
u32 waitbase, struct nvhost_channel *ch)
|
|
{
|
|
struct host1x_hwctx_handler *p;
|
|
|
|
p = kmalloc(sizeof(*p), GFP_KERNEL);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
p->h.syncpt = syncpt;
|
|
p->h.waitbase = waitbase;
|
|
|
|
p->h.alloc = vic03_alloc_hwctx;
|
|
p->h.get = vic03_get_hwctx;
|
|
p->h.put = vic03_put_hwctx;
|
|
p->h.save_push = vic03_save_push_hwctx;
|
|
p->h.restore_push = ctxvic03_restore_push;
|
|
|
|
return &p->h;
|
|
}
|
|
|
|
int nvhost_vic03_finalize_poweron(struct platform_device *pdev)
|
|
{
|
|
struct vic03 *v = get_vic03(pdev);
|
|
vic03_writel(v, flcn_slcg_override_high_a_r(), 0);
|
|
vic03_writel(v, flcn_cg_r(),
|
|
flcn_cg_idle_cg_dly_cnt_f(4) |
|
|
flcn_cg_idle_cg_en_f(1) |
|
|
flcn_cg_wakeup_dly_cnt_f(4));
|
|
return vic03_boot(pdev);
|
|
}
|
|
|
|
int nvhost_vic03_prepare_poweroff(struct platform_device *dev)
|
|
{
|
|
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
|
|
struct vic03 *v;
|
|
struct nvhost_channel *ch = pdata->channel;
|
|
|
|
if (ch) {
|
|
mutex_lock(&ch->submitlock);
|
|
ch->cur_ctx = NULL;
|
|
mutex_unlock(&ch->submitlock);
|
|
}
|
|
|
|
v = get_vic03(pdata->pdev);
|
|
if (v)
|
|
v->is_booted = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct of_device_id tegra_vic_of_match[] = {
|
|
{ .compatible = "nvidia,tegra124-vic",
|
|
.data = (struct nvhost_device_data *)&t124_vic_info },
|
|
{ },
|
|
};
|
|
|
|
static int vic03_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_vic_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 = nvhost_client_device_get_resources(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
dev->dev.platform_data = NULL;
|
|
|
|
nvhost_module_init(dev);
|
|
|
|
#ifdef CONFIG_PM_GENERIC_DOMAINS
|
|
pdata->pd.name = "vic03";
|
|
|
|
err = nvhost_module_add_domain(&pdata->pd, dev);
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
err = nvhost_as_init_device(dev);
|
|
if (err) {
|
|
nvhost_dbg_fn(
|
|
"failed to init client address space device for %s",
|
|
dev->name);
|
|
pm_runtime_put(&dev->dev);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __exit vic03_remove(struct platform_device *dev)
|
|
{
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
pm_runtime_put(&dev->dev);
|
|
pm_runtime_disable(&dev->dev);
|
|
#else
|
|
nvhost_module_disable_clk(&dev->dev);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver vic03_driver = {
|
|
.probe = vic03_probe,
|
|
.remove = __exit_p(vic03_remove),
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "vic03",
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = tegra_vic_of_match,
|
|
#endif
|
|
#ifdef CONFIG_PM
|
|
.pm = &nvhost_module_pm_ops,
|
|
#endif
|
|
}
|
|
};
|
|
|
|
static int __init vic03_init(void)
|
|
{
|
|
return platform_driver_register(&vic03_driver);
|
|
}
|
|
|
|
static void __exit vic03_exit(void)
|
|
{
|
|
platform_driver_unregister(&vic03_driver);
|
|
}
|
|
|
|
module_init(vic03_init);
|
|
module_exit(vic03_exit);
|