539 lines
15 KiB
C
539 lines
15 KiB
C
/*
|
|
* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
* Copyright 2018 NXP
|
|
*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ipu.h>
|
|
#include <linux/genalloc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "vdoa.h"
|
|
/* 6band(3field* double buffer) * (width*2) * bandline(8)
|
|
= 6x1024x2x8 = 96k or 72k(1.5byte) */
|
|
#define MAX_VDOA_IRAM_SIZE (1024*96)
|
|
#define VDOA_IRAM_SIZE (1024*72)
|
|
|
|
#define VDOAC_BAND_HEIGHT_32LINES (32)
|
|
#define VDOAC_BAND_HEIGHT_16LINES (16)
|
|
#define VDOAC_BAND_HEIGHT_8LINES (8)
|
|
#define VDOAC_THREE_FRAMES (0x1 << 2)
|
|
#define VDOAC_SYNC_BAND_MODE (0x1 << 3)
|
|
#define VDOAC_SCAN_ORDER_INTERLACED (0x1 << 4)
|
|
#define VDOAC_PFS_YUYV (0x1 << 5)
|
|
#define VDOAC_IPU_SEL_1 (0x1 << 6)
|
|
#define VDOAFP_FH_MASK (0x1FFF)
|
|
#define VDOAFP_FH_SHIFT (16)
|
|
#define VDOAFP_FW_MASK (0x3FFF)
|
|
#define VDOAFP_FW_SHIFT (0)
|
|
#define VDOASL_VSLY_MASK (0x3FFF)
|
|
#define VDOASL_VSLY_SHIFT (16)
|
|
#define VDOASL_ISLY_MASK (0x7FFF)
|
|
#define VDOASL_ISLY_SHIFT (0)
|
|
#define VDOASRR_START_XFER (0x2)
|
|
#define VDOASRR_SWRST (0x1)
|
|
#define VDOAIEIST_TRANSFER_ERR (0x2)
|
|
#define VDOAIEIST_TRANSFER_END (0x1)
|
|
|
|
#define VDOAC (0x0) /* Control Register */
|
|
#define VDOASRR (0x4) /* Start and Reset Register */
|
|
#define VDOAIE (0x8) /* Interrupt Enable Register */
|
|
#define VDOAIST (0xc) /* Interrupt Status Register */
|
|
#define VDOAFP (0x10) /* Frame Parameters Register */
|
|
#define VDOAIEBA00 (0x14) /* External Buffer n Frame m Address Register */
|
|
#define VDOAIEBA01 (0x18) /* External Buffer n Frame m Address Register */
|
|
#define VDOAIEBA02 (0x1c) /* External Buffer n Frame m Address Register */
|
|
#define VDOAIEBA10 (0x20) /* External Buffer n Frame m Address Register */
|
|
#define VDOAIEBA11 (0x24) /* External Buffer n Frame m Address Register */
|
|
#define VDOAIEBA12 (0x28) /* External Buffer n Frame m Address Register */
|
|
#define VDOASL (0x2c) /* IPU Stride Line Register */
|
|
#define VDOAIUBO (0x30) /* IPU Chroma Buffer Offset Register */
|
|
#define VDOAVEBA0 (0x34) /* External Buffer m Address Register */
|
|
#define VDOAVEBA1 (0x38) /* External Buffer m Address Register */
|
|
#define VDOAVEBA2 (0x3c) /* External Buffer m Address Register */
|
|
#define VDOAVUBO (0x40) /* VPU Chroma Buffer Offset */
|
|
#define VDOASR (0x44) /* Status Register */
|
|
#define VDOATD (0x48) /* Test Debug Register */
|
|
|
|
|
|
enum {
|
|
VDOA_INIT = 0x1,
|
|
VDOA_GET = 0x2,
|
|
VDOA_SETUP = 0x4,
|
|
VDOA_GET_OBUF = 0x8,
|
|
VDOA_START = 0x10,
|
|
VDOA_INIRQ = 0x20,
|
|
VDOA_STOP = 0x40,
|
|
VDOA_PUT = VDOA_INIT,
|
|
};
|
|
|
|
enum {
|
|
VDOA_NULL = 0,
|
|
VDOA_FRAME = 1,
|
|
VDOA_PREV_FIELD = 2,
|
|
VDOA_CURR_FIELD = 3,
|
|
VDOA_NEXT_FIELD = 4,
|
|
};
|
|
|
|
#define CHECK_STATE(expect, retcode) \
|
|
do { \
|
|
if (!((expect) & vdoa->state)) { \
|
|
dev_err(vdoa->dev, "ERR: %s state:0x%x, expect:0x%x.\n",\
|
|
__func__, vdoa->state, (expect)); \
|
|
retcode; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHECK_NULL_PTR(ptr) \
|
|
do { \
|
|
pr_debug("vdoa_ptr:0x%p in %s state:0x%x.\n", \
|
|
vdoa, __func__, vdoa->state); \
|
|
if (NULL == (ptr)) { \
|
|
pr_err("ERR vdoa: %s state:0x%x null ptr.\n", \
|
|
__func__, vdoa->state); \
|
|
} \
|
|
} while (0)
|
|
|
|
struct vdoa_info {
|
|
int state;
|
|
struct device *dev;
|
|
struct clk *vdoa_clk;
|
|
void __iomem *reg_base;
|
|
struct gen_pool *iram_pool;
|
|
unsigned long iram_base;
|
|
unsigned long iram_paddr;
|
|
int irq;
|
|
int field;
|
|
struct completion comp;
|
|
};
|
|
|
|
static struct vdoa_info *g_vdoa;
|
|
static unsigned long iram_size;
|
|
static DEFINE_MUTEX(vdoa_lock);
|
|
|
|
static inline void vdoa_read_register(struct vdoa_info *vdoa,
|
|
u32 reg, u32 *val)
|
|
{
|
|
*val = ioread32(vdoa->reg_base + reg);
|
|
dev_dbg(vdoa->dev, "read_reg:0x%02x, val:0x%08x.\n", reg, *val);
|
|
}
|
|
|
|
static inline void vdoa_write_register(struct vdoa_info *vdoa,
|
|
u32 reg, u32 val)
|
|
{
|
|
iowrite32(val, vdoa->reg_base + reg);
|
|
dev_dbg(vdoa->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n", reg, val);
|
|
}
|
|
|
|
static void dump_registers(struct vdoa_info *vdoa)
|
|
{
|
|
int i;
|
|
u32 data;
|
|
|
|
for (i = VDOAC; i < VDOATD; i += 4)
|
|
vdoa_read_register(vdoa, i, &data);
|
|
}
|
|
|
|
int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
|
|
{
|
|
int band_size;
|
|
int total_band_size = 0;
|
|
int ipu_stride;
|
|
u32 data;
|
|
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return -EINVAL);
|
|
if (VDOA_GET == vdoa->state) {
|
|
dev_dbg(vdoa->dev, "w:%d, h:%d.\n",
|
|
params->width, params->height);
|
|
data = (params->band_lines == VDOAC_BAND_HEIGHT_32LINES) ? 2 :
|
|
((params->band_lines == VDOAC_BAND_HEIGHT_16LINES) ?
|
|
1 : 0);
|
|
data |= params->scan_order ? VDOAC_SCAN_ORDER_INTERLACED : 0;
|
|
data |= params->band_mode ? VDOAC_SYNC_BAND_MODE : 0;
|
|
data |= params->pfs ? VDOAC_PFS_YUYV : 0;
|
|
data |= params->ipu_num ? VDOAC_IPU_SEL_1 : 0;
|
|
vdoa_write_register(vdoa, VDOAC, data);
|
|
|
|
data = ((params->width & VDOAFP_FW_MASK) << VDOAFP_FW_SHIFT) |
|
|
((params->height & VDOAFP_FH_MASK) << VDOAFP_FH_SHIFT);
|
|
vdoa_write_register(vdoa, VDOAFP, data);
|
|
|
|
ipu_stride = params->pfs ? params->width << 1 : params->width;
|
|
data = ((params->vpu_stride & VDOASL_VSLY_MASK) <<
|
|
VDOASL_VSLY_SHIFT) |
|
|
((ipu_stride & VDOASL_ISLY_MASK) << VDOASL_ISLY_SHIFT);
|
|
vdoa_write_register(vdoa, VDOASL, data);
|
|
|
|
dev_dbg(vdoa->dev, "band_mode:%d, band_line:%d, base:0x%lx.\n",
|
|
params->band_mode, params->band_lines, vdoa->iram_paddr);
|
|
}
|
|
/*
|
|
* band size = (luma_per_line + chroma_per_line) * bandLines
|
|
* = width * (3/2 or 2) * bandLines
|
|
* double buffer mode used.
|
|
*/
|
|
if (params->pfs)
|
|
band_size = (params->width << 1) * params->band_lines;
|
|
else
|
|
band_size = ((params->width * 3) >> 1) *
|
|
params->band_lines;
|
|
if (params->interlaced) {
|
|
total_band_size = 6 * band_size; /* 3 frames*double buffer */
|
|
if (iram_size < total_band_size) {
|
|
dev_err(vdoa->dev, "iram_size:0x%lx is smaller than "
|
|
"request:0x%x!\n", iram_size, total_band_size);
|
|
return -EINVAL;
|
|
}
|
|
if (params->vfield_buf.prev_veba) {
|
|
if (params->band_mode) {
|
|
vdoa_write_register(vdoa, VDOAIEBA00,
|
|
vdoa->iram_paddr);
|
|
vdoa_write_register(vdoa, VDOAIEBA10,
|
|
vdoa->iram_paddr + band_size);
|
|
} else
|
|
vdoa_write_register(vdoa, VDOAIEBA00,
|
|
params->ieba0);
|
|
vdoa_write_register(vdoa, VDOAVEBA0,
|
|
params->vfield_buf.prev_veba);
|
|
vdoa->field = VDOA_PREV_FIELD;
|
|
}
|
|
if (params->vfield_buf.cur_veba) {
|
|
if (params->band_mode) {
|
|
vdoa_write_register(vdoa, VDOAIEBA01,
|
|
vdoa->iram_paddr + band_size * 2);
|
|
vdoa_write_register(vdoa, VDOAIEBA11,
|
|
vdoa->iram_paddr + band_size * 3);
|
|
} else
|
|
vdoa_write_register(vdoa, VDOAIEBA01,
|
|
params->ieba1);
|
|
vdoa_write_register(vdoa, VDOAVEBA1,
|
|
params->vfield_buf.cur_veba);
|
|
vdoa->field = VDOA_CURR_FIELD;
|
|
}
|
|
if (params->vfield_buf.next_veba) {
|
|
if (params->band_mode) {
|
|
vdoa_write_register(vdoa, VDOAIEBA02,
|
|
vdoa->iram_paddr + band_size * 4);
|
|
vdoa_write_register(vdoa, VDOAIEBA12,
|
|
vdoa->iram_paddr + band_size * 5);
|
|
} else
|
|
vdoa_write_register(vdoa, VDOAIEBA02,
|
|
params->ieba2);
|
|
vdoa_write_register(vdoa, VDOAVEBA2,
|
|
params->vfield_buf.next_veba);
|
|
vdoa->field = VDOA_NEXT_FIELD;
|
|
vdoa_read_register(vdoa, VDOAC, &data);
|
|
data |= VDOAC_THREE_FRAMES;
|
|
vdoa_write_register(vdoa, VDOAC, data);
|
|
}
|
|
|
|
if (!params->pfs)
|
|
vdoa_write_register(vdoa, VDOAIUBO,
|
|
params->width * params->band_lines);
|
|
vdoa_write_register(vdoa, VDOAVUBO,
|
|
params->vfield_buf.vubo);
|
|
dev_dbg(vdoa->dev, "total band_size:0x%x.\n", band_size*6);
|
|
} else if (params->band_mode) {
|
|
/* used for progressive frame resize on PrP channel */
|
|
BUG(); /* currently not support */
|
|
/* progressvie frame: band mode */
|
|
vdoa_write_register(vdoa, VDOAIEBA00, vdoa->iram_paddr);
|
|
vdoa_write_register(vdoa, VDOAIEBA10,
|
|
vdoa->iram_paddr + band_size);
|
|
if (!params->pfs)
|
|
vdoa_write_register(vdoa, VDOAIUBO,
|
|
params->width * params->band_lines);
|
|
dev_dbg(vdoa->dev, "total band_size:0x%x\n", band_size*2);
|
|
} else {
|
|
/* progressive frame: mem->mem, non-band mode */
|
|
vdoa->field = VDOA_FRAME;
|
|
vdoa_write_register(vdoa, VDOAVEBA0, params->vframe_buf.veba);
|
|
vdoa_write_register(vdoa, VDOAVUBO, params->vframe_buf.vubo);
|
|
vdoa_write_register(vdoa, VDOAIEBA00, params->ieba0);
|
|
if (!params->pfs)
|
|
/* note: iubo is relative value, based on ieba0 */
|
|
vdoa_write_register(vdoa, VDOAIUBO,
|
|
params->width * params->height);
|
|
}
|
|
vdoa->state = VDOA_SETUP;
|
|
return 0;
|
|
}
|
|
|
|
void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf)
|
|
{
|
|
u32 data;
|
|
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_SETUP, return);
|
|
vdoa->state = VDOA_GET_OBUF;
|
|
memset(buf, 0, sizeof(*buf));
|
|
|
|
vdoa_read_register(vdoa, VDOAC, &data);
|
|
switch (vdoa->field) {
|
|
case VDOA_FRAME:
|
|
case VDOA_PREV_FIELD:
|
|
vdoa_read_register(vdoa, VDOAIEBA00, &buf->ieba0);
|
|
if (data & VDOAC_SYNC_BAND_MODE)
|
|
vdoa_read_register(vdoa, VDOAIEBA10, &buf->ieba1);
|
|
break;
|
|
case VDOA_CURR_FIELD:
|
|
vdoa_read_register(vdoa, VDOAIEBA01, &buf->ieba0);
|
|
vdoa_read_register(vdoa, VDOAIEBA11, &buf->ieba1);
|
|
break;
|
|
case VDOA_NEXT_FIELD:
|
|
vdoa_read_register(vdoa, VDOAIEBA02, &buf->ieba0);
|
|
vdoa_read_register(vdoa, VDOAIEBA12, &buf->ieba1);
|
|
break;
|
|
default:
|
|
BUG();
|
|
break;
|
|
}
|
|
if (!(data & VDOAC_PFS_YUYV))
|
|
vdoa_read_register(vdoa, VDOAIUBO, &buf->iubo);
|
|
}
|
|
|
|
int vdoa_start(vdoa_handle_t handle, int timeout_ms)
|
|
{
|
|
int ret;
|
|
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_GET_OBUF, return -EINVAL);
|
|
vdoa->state = VDOA_START;
|
|
init_completion(&vdoa->comp);
|
|
vdoa_write_register(vdoa, VDOAIST,
|
|
VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
|
|
vdoa_write_register(vdoa, VDOAIE,
|
|
VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
|
|
|
|
enable_irq(vdoa->irq);
|
|
vdoa_write_register(vdoa, VDOASRR, VDOASRR_START_XFER);
|
|
dump_registers(vdoa);
|
|
|
|
ret = wait_for_completion_timeout(&vdoa->comp,
|
|
msecs_to_jiffies(timeout_ms));
|
|
|
|
return ret > 0 ? 0 : -ETIMEDOUT;
|
|
}
|
|
|
|
void vdoa_stop(vdoa_handle_t handle)
|
|
{
|
|
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_GET | VDOA_START | VDOA_INIRQ, return);
|
|
vdoa->state = VDOA_STOP;
|
|
|
|
disable_irq(vdoa->irq);
|
|
|
|
vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
|
|
}
|
|
|
|
void vdoa_get_handle(vdoa_handle_t *handle)
|
|
{
|
|
struct vdoa_info *vdoa = g_vdoa;
|
|
|
|
CHECK_NULL_PTR(handle);
|
|
*handle = (vdoa_handle_t *)NULL;
|
|
CHECK_STATE(VDOA_INIT, return);
|
|
mutex_lock(&vdoa_lock);
|
|
clk_prepare_enable(vdoa->vdoa_clk);
|
|
vdoa->state = VDOA_GET;
|
|
vdoa->field = VDOA_NULL;
|
|
vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
|
|
|
|
*handle = (vdoa_handle_t *)vdoa;
|
|
}
|
|
|
|
void vdoa_put_handle(vdoa_handle_t *handle)
|
|
{
|
|
struct vdoa_info *vdoa = (struct vdoa_info *)(*handle);
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_STOP, return);
|
|
if (vdoa != g_vdoa)
|
|
BUG();
|
|
|
|
clk_disable_unprepare(vdoa->vdoa_clk);
|
|
vdoa->state = VDOA_PUT;
|
|
*handle = (vdoa_handle_t *)NULL;
|
|
mutex_unlock(&vdoa_lock);
|
|
}
|
|
|
|
static irqreturn_t vdoa_irq_handler(int irq, void *data)
|
|
{
|
|
u32 status, mask, val;
|
|
struct vdoa_info *vdoa = data;
|
|
|
|
CHECK_NULL_PTR(vdoa);
|
|
CHECK_STATE(VDOA_START, return IRQ_HANDLED);
|
|
vdoa->state = VDOA_INIRQ;
|
|
vdoa_read_register(vdoa, VDOAIST, &status);
|
|
vdoa_read_register(vdoa, VDOAIE, &mask);
|
|
val = status & mask;
|
|
vdoa_write_register(vdoa, VDOAIST, val);
|
|
if (VDOAIEIST_TRANSFER_ERR & val)
|
|
dev_err(vdoa->dev, "vdoa Transfer err irq!\n");
|
|
if (VDOAIEIST_TRANSFER_END & val)
|
|
dev_dbg(vdoa->dev, "vdoa Transfer end irq!\n");
|
|
if (0 == val) {
|
|
dev_err(vdoa->dev, "vdoa unknown irq!\n");
|
|
BUG();
|
|
}
|
|
|
|
complete(&vdoa->comp);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* IRAM Size in Kbytes, example:vdoa_iram_size=64, 64KBytes */
|
|
static int __init vdoa_iram_size_setup(char *options)
|
|
{
|
|
int ret;
|
|
|
|
ret = kstrtoul(options, 0, &iram_size);
|
|
if (ret)
|
|
iram_size = 0;
|
|
else
|
|
iram_size *= SZ_1K;
|
|
|
|
return 1;
|
|
}
|
|
__setup("vdoa_iram_size=", vdoa_iram_size_setup);
|
|
|
|
static const struct of_device_id imx_vdoa_dt_ids[] = {
|
|
{ .compatible = "fsl,imx6q-vdoa", },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static int vdoa_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct vdoa_info *vdoa;
|
|
struct resource *res;
|
|
struct resource *res_irq;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_err(dev, "can't get device resources\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
if (!res_irq) {
|
|
dev_err(dev, "failed to get irq resource\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
vdoa = devm_kzalloc(dev, sizeof(struct vdoa_info), GFP_KERNEL);
|
|
if (!vdoa)
|
|
return -ENOMEM;
|
|
vdoa->dev = dev;
|
|
|
|
vdoa->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (!vdoa->reg_base)
|
|
return -EBUSY;
|
|
|
|
vdoa->irq = res_irq->start;
|
|
ret = devm_request_irq(dev, vdoa->irq, vdoa_irq_handler, 0,
|
|
"vdoa", vdoa);
|
|
if (ret) {
|
|
dev_err(dev, "can't claim irq %d\n", vdoa->irq);
|
|
return ret;
|
|
}
|
|
disable_irq(vdoa->irq);
|
|
|
|
vdoa->vdoa_clk = devm_clk_get(dev, NULL);
|
|
if (IS_ERR(vdoa->vdoa_clk)) {
|
|
dev_err(dev, "failed to get vdoa_clk\n");
|
|
return PTR_ERR(vdoa->vdoa_clk);
|
|
}
|
|
|
|
vdoa->iram_pool = of_gen_pool_get(np, "iram", 0);
|
|
if (!vdoa->iram_pool) {
|
|
dev_err(&pdev->dev, "iram pool not available\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if ((iram_size == 0) || (iram_size > MAX_VDOA_IRAM_SIZE))
|
|
iram_size = VDOA_IRAM_SIZE;
|
|
|
|
vdoa->iram_base = gen_pool_alloc(vdoa->iram_pool, iram_size);
|
|
if (!vdoa->iram_base) {
|
|
dev_err(&pdev->dev, "unable to alloc iram\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
vdoa->iram_paddr = gen_pool_virt_to_phys(vdoa->iram_pool,
|
|
vdoa->iram_base);
|
|
|
|
dev_dbg(dev, "iram_base:0x%lx,iram_paddr:0x%lx,size:0x%lx\n",
|
|
vdoa->iram_base, vdoa->iram_paddr, iram_size);
|
|
|
|
vdoa->state = VDOA_INIT;
|
|
dev_set_drvdata(dev, vdoa);
|
|
g_vdoa = vdoa;
|
|
dev_info(dev, "i.MX Video Data Order Adapter(VDOA) driver probed\n");
|
|
return 0;
|
|
}
|
|
|
|
static int vdoa_remove(struct platform_device *pdev)
|
|
{
|
|
struct vdoa_info *vdoa = dev_get_drvdata(&pdev->dev);
|
|
|
|
gen_pool_free(vdoa->iram_pool, vdoa->iram_base, iram_size);
|
|
kfree(vdoa);
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver vdoa_driver = {
|
|
.driver = {
|
|
.name = "mxc_vdoa",
|
|
.of_match_table = imx_vdoa_dt_ids,
|
|
},
|
|
.probe = vdoa_probe,
|
|
.remove = vdoa_remove,
|
|
};
|
|
|
|
static int __init vdoa_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = platform_driver_register(&vdoa_driver);
|
|
if (err) {
|
|
pr_err("vdoa_driver register failed\n");
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void __exit vdoa_cleanup(void)
|
|
{
|
|
platform_driver_unregister(&vdoa_driver);
|
|
}
|
|
|
|
subsys_initcall(vdoa_init);
|
|
module_exit(vdoa_cleanup);
|
|
|
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
|
MODULE_DESCRIPTION("i.MX Video Data Order Adapter(VDOA) driver");
|
|
MODULE_LICENSE("GPL");
|