|
|
|
|
@@ -0,0 +1,947 @@
|
|
|
|
|
/*
|
|
|
|
|
* drivers/amlogic/media/video_processor/video_dev/amlvideo.c
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that 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.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/delay.h>
|
|
|
|
|
#include <linux/errno.h>
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/wait.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include <linux/mm.h>
|
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
#include <linux/sched.h>
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
|
#include <linux/random.h>
|
|
|
|
|
#include <linux/version.h>
|
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
#include <linux/kthread.h>
|
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
|
#include <linux/freezer.h>
|
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
#include <linux/amlogic/media/v4l_util/videobuf-res.h>
|
|
|
|
|
#include <media/videobuf2-core.h>
|
|
|
|
|
#include <media/v4l2-device.h>
|
|
|
|
|
#include <media/v4l2-ioctl.h>
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
|
#include <linux/amlogic/media/canvas/canvas.h>
|
|
|
|
|
#include <linux/amlogic/media/vfm/vframe.h>
|
|
|
|
|
#include <linux/amlogic/media/vfm/vframe_provider.h>
|
|
|
|
|
#include <linux/amlogic/media/vfm/vframe_receiver.h>
|
|
|
|
|
#include <linux/amlogic/media/ge2d/ge2d.h>
|
|
|
|
|
#include <linux/amlogic/media/frame_sync/timestamp.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/amlogic/media/frame_sync/tsync.h>
|
|
|
|
|
#include "common/vfp.h"
|
|
|
|
|
#include "amlvideo.h"
|
|
|
|
|
|
|
|
|
|
#define AVMLVIDEO_MODULE_NAME "amlvideo"
|
|
|
|
|
|
|
|
|
|
#define AMLVIDEO_INFO(fmt, args...) pr_info("amlvid:info: "fmt"", ## args)
|
|
|
|
|
#define AMLVIDEO_DBG(fmt, args...) pr_debug("amlvid:dbg: "fmt"", ## args)
|
|
|
|
|
#define AMLVIDEO_WARN(fmt, args...) pr_warn("amlvid:warn: "fmt"", ## args)
|
|
|
|
|
#define AMLVIDEO_ERR(fmt, args...) pr_err("amlvid:err: "fmt"", ## args)
|
|
|
|
|
|
|
|
|
|
#define AMLVIDEO_MAJOR_VERSION 0
|
|
|
|
|
#define AMLVIDEO_MINOR_VERSION 7
|
|
|
|
|
#define AMLVIDEO_RELEASE 1
|
|
|
|
|
#define AMLVIDEO_VERSION \
|
|
|
|
|
KERNEL_VERSION(AMLVIDEO_MAJOR_VERSION, \
|
|
|
|
|
AMLVIDEO_MINOR_VERSION, AMLVIDEO_RELEASE)
|
|
|
|
|
#define MAGIC_RE_MEM 0x123039dc
|
|
|
|
|
|
|
|
|
|
#define RECEIVER_NAME "amlvideo"
|
|
|
|
|
#define PROVIDER_NAME "amlvideo"
|
|
|
|
|
|
|
|
|
|
#define AMLVIDEO_POOL_SIZE 16
|
|
|
|
|
/*extern bool omx_secret_mode;*/
|
|
|
|
|
|
|
|
|
|
#define DUR2PTS(x) ((x) - ((x) >> 4))
|
|
|
|
|
#define DUR2PTS_RM(x) ((x) & 0xf)
|
|
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("pass a frame of amlogic video codec device to user in style of v4l2");
|
|
|
|
|
MODULE_AUTHOR("amlogic-sh");
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
/* static u32 vpts_remainder; */
|
|
|
|
|
static unsigned int video_nr_base = 10;
|
|
|
|
|
/* module_param(video_nr_base, uint, 0644); */
|
|
|
|
|
/* MODULE_PARM_DESC(video_nr_base, "videoX start number, 10 is defaut"); */
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_MULTI_DEC
|
|
|
|
|
static unsigned int n_devs = 1;
|
|
|
|
|
#else
|
|
|
|
|
static unsigned int n_devs = 1;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static unsigned int debug;
|
|
|
|
|
/* module_param(debug, uint, 0644); */
|
|
|
|
|
/* MODULE_PARM_DESC(debug, "activates debug info"); */
|
|
|
|
|
|
|
|
|
|
static unsigned int vid_limit = 16;
|
|
|
|
|
module_param(vid_limit, uint, 0644);
|
|
|
|
|
MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
|
|
|
|
|
|
|
|
|
|
static int video_receiver_event_fun(int type, void *data, void*);
|
|
|
|
|
|
|
|
|
|
#define dprintk(dev, level, fmt, arg...) \
|
|
|
|
|
v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
|
* Basic structures
|
|
|
|
|
* ------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static struct vivi_fmt formats[] = {
|
|
|
|
|
{
|
|
|
|
|
.name = "RGB888 (24)",
|
|
|
|
|
.fourcc = V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */
|
|
|
|
|
.depth = 24,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.name = "RGBA8888 (32)",
|
|
|
|
|
.fourcc = V4L2_PIX_FMT_RGB32, /* 24 RGBA-8-8-8-8 */
|
|
|
|
|
.depth = 32,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.name = "12 Y/CbCr 4:2:0",
|
|
|
|
|
.fourcc = V4L2_PIX_FMT_NV12,
|
|
|
|
|
.depth = 12,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.name = "21 Y/CbCr 4:2:0",
|
|
|
|
|
.fourcc = V4L2_PIX_FMT_NV21,
|
|
|
|
|
.depth = 12,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.name = "RGB565 (BE)",
|
|
|
|
|
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
|
|
|
|
|
.depth = 16,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* -----------------------------------------------------------------
|
|
|
|
|
* provider operations
|
|
|
|
|
* -----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
static struct vframe_s *amlvideo_vf_peek(void *op_arg)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev = (struct vivi_dev *)op_arg;
|
|
|
|
|
|
|
|
|
|
return vfq_peek(&dev->q_ready);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct vframe_s *amlvideo_vf_get(void *op_arg)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev = (struct vivi_dev *)op_arg;
|
|
|
|
|
|
|
|
|
|
return vfq_pop(&dev->q_ready);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void amlvideo_vf_put(struct vframe_s *vf, void *op_arg)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev = (struct vivi_dev *)op_arg;
|
|
|
|
|
|
|
|
|
|
vf_put(vf, dev->vf_receiver_name);
|
|
|
|
|
vf_notify_provider(dev->vf_receiver_name, VFRAME_EVENT_RECEIVER_PUT,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_event_cb(int type, void *data, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
/* printk("ionvideo_event_cb_type=%d\n",type); */
|
|
|
|
|
if (type & VFRAME_EVENT_RECEIVER_PUT) {
|
|
|
|
|
/* printk("video put, avail=%d\n", vfq_level(&q_ready) ); */
|
|
|
|
|
} else if (type & VFRAME_EVENT_RECEIVER_GET) {
|
|
|
|
|
/* printk("video get, avail=%d\n", vfq_level(&q_ready) ); */
|
|
|
|
|
} else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT) {
|
|
|
|
|
/* up(&thread_sem); */
|
|
|
|
|
/* printk("receiver is waiting\n"); */
|
|
|
|
|
} else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT) {
|
|
|
|
|
/* printk("frame wait\n"); */
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_vf_states(struct vframe_states *states, void *op_arg)
|
|
|
|
|
{
|
|
|
|
|
/* unsigned long flags; */
|
|
|
|
|
/* spin_lock_irqsave(&lock, flags); */
|
|
|
|
|
struct vivi_dev *dev = (struct vivi_dev *)op_arg;
|
|
|
|
|
|
|
|
|
|
states->vf_pool_size = AMLVIDEO_POOL_SIZE;
|
|
|
|
|
states->buf_recycle_num = 0;
|
|
|
|
|
states->buf_free_num = AMLVIDEO_POOL_SIZE - vfq_level(&dev->q_ready);
|
|
|
|
|
states->buf_avail_num = vfq_level(&dev->q_ready);
|
|
|
|
|
/* spin_unlock_irqrestore(&lock, flags); */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct vframe_operations_s amlvideo_vf_provider = {
|
|
|
|
|
.peek = amlvideo_vf_peek,
|
|
|
|
|
.get = amlvideo_vf_get,
|
|
|
|
|
.put = amlvideo_vf_put,
|
|
|
|
|
.event_cb = amlvideo_event_cb,
|
|
|
|
|
.vf_states = amlvideo_vf_states,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct vivi_fmt *get_format(struct v4l2_format *f)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fmt *fmt;
|
|
|
|
|
unsigned int k;
|
|
|
|
|
|
|
|
|
|
for (k = 0; k < ARRAY_SIZE(formats); k++) {
|
|
|
|
|
fmt = &formats[k];
|
|
|
|
|
if (fmt->fourcc == f->fmt.pix.pixelformat)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (k == ARRAY_SIZE(formats))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return &formats[k];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int video_receiver_event_fun(int type, void *data, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
struct vframe_states states;
|
|
|
|
|
struct vivi_dev *dev = (struct vivi_dev *)private_data;
|
|
|
|
|
|
|
|
|
|
if (type == VFRAME_EVENT_PROVIDER_UNREG) {
|
|
|
|
|
AMLVIDEO_DBG("AML:VFRAME_EVENT_PROVIDER_UNREG\n");
|
|
|
|
|
if (vf_get_receiver(dev->vf_provider_name)) {
|
|
|
|
|
AMLVIDEO_DBG("unreg:amlvideo\n");
|
|
|
|
|
vf_unreg_provider(&dev->video_vf_prov);
|
|
|
|
|
omx_secret_mode = false;
|
|
|
|
|
}
|
|
|
|
|
dev->first_frame = 0;
|
|
|
|
|
vfq_init(&dev->q_ready, AMLVIDEO_POOL_SIZE + 1,
|
|
|
|
|
&dev->amlvideo_pool_ready[0]);
|
|
|
|
|
}
|
|
|
|
|
if (type == VFRAME_EVENT_PROVIDER_REG) {
|
|
|
|
|
AMLVIDEO_DBG("AML:VFRAME_EVENT_PROVIDER_REG\n");
|
|
|
|
|
|
|
|
|
|
dev->vf = NULL;
|
|
|
|
|
dev->first_frame = 0;
|
|
|
|
|
} else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) {
|
|
|
|
|
amlvideo_vf_states(&states, dev);
|
|
|
|
|
if (states.buf_avail_num > 0)
|
|
|
|
|
return RECEIVER_ACTIVE;
|
|
|
|
|
if (vf_notify_receiver(
|
|
|
|
|
dev->vf_provider_name,
|
|
|
|
|
VFRAME_EVENT_PROVIDER_QUREY_STATE,
|
|
|
|
|
NULL) == RECEIVER_ACTIVE)
|
|
|
|
|
return RECEIVER_ACTIVE;
|
|
|
|
|
return RECEIVER_INACTIVE;
|
|
|
|
|
|
|
|
|
|
/*break;*/
|
|
|
|
|
} else if (type == VFRAME_EVENT_PROVIDER_START) {
|
|
|
|
|
AMLVIDEO_DBG("AML:VFRAME_EVENT_PROVIDER_START\n");
|
|
|
|
|
if (vf_get_receiver(dev->vf_provider_name)) {
|
|
|
|
|
struct vframe_receiver_s *aaa = vf_get_receiver(
|
|
|
|
|
dev->vf_provider_name);
|
|
|
|
|
AMLVIDEO_DBG("aaa->name=%s", aaa->name);
|
|
|
|
|
omx_secret_mode = true;
|
|
|
|
|
vfq_init(&dev->q_ready, AMLVIDEO_POOL_SIZE + 1,
|
|
|
|
|
&dev->amlvideo_pool_ready[0]);
|
|
|
|
|
vf_provider_init(&dev->video_vf_prov,
|
|
|
|
|
dev->vf_provider_name,
|
|
|
|
|
&amlvideo_vf_provider, dev);
|
|
|
|
|
vf_reg_provider(&dev->video_vf_prov);
|
|
|
|
|
vf_notify_receiver(dev->vf_provider_name,
|
|
|
|
|
VFRAME_EVENT_PROVIDER_START,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct vframe_receiver_op_s video_vf_receiver = {
|
|
|
|
|
.event_cb = video_receiver_event_fun
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
|
* Videobuf operations
|
|
|
|
|
* ------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
|
|
|
|
|
unsigned int *size)
|
|
|
|
|
{
|
|
|
|
|
struct videobuf_res_privdata *res = (struct videobuf_res_privdata *)vq
|
|
|
|
|
->priv_data;
|
|
|
|
|
struct vivi_fh *fh = (struct vivi_fh *)res->priv;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
*size = (fh->width * fh->height * fh->fmt->depth) >> 3;
|
|
|
|
|
if (*count == 0)
|
|
|
|
|
*count = 32;
|
|
|
|
|
|
|
|
|
|
while (*size * *count > vid_limit * 1024 * 1024)
|
|
|
|
|
(*count)--;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, *count, *size);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
|
|
|
|
|
{
|
|
|
|
|
struct videobuf_res_privdata *res = (struct videobuf_res_privdata *)vq
|
|
|
|
|
->priv_data;
|
|
|
|
|
struct vivi_fh *fh = (struct vivi_fh *)res->priv;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
|
|
|
|
|
videobuf_waiton(vq, &buf->vb, 0, 0);
|
|
|
|
|
if (in_interrupt())
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
videobuf_res_free(vq, &buf->vb);
|
|
|
|
|
dprintk(dev, 1, "free_buffer: freed\n");
|
|
|
|
|
buf->vb.state = VIDEOBUF_NEEDS_INIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define NORM_MAXW 2000
|
|
|
|
|
#define NORM_MAXH 1600
|
|
|
|
|
static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
|
|
|
|
enum v4l2_field field)
|
|
|
|
|
{
|
|
|
|
|
struct videobuf_res_privdata *res = (struct videobuf_res_privdata *)vq
|
|
|
|
|
->priv_data;
|
|
|
|
|
struct vivi_fh *fh = (struct vivi_fh *)res->priv;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
struct vivi_buffer
|
|
|
|
|
*buf = container_of(vb, struct vivi_buffer, vb);
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "%s, field=%d\n", __func__, field);
|
|
|
|
|
|
|
|
|
|
WARN_ON(fh->fmt == NULL);
|
|
|
|
|
|
|
|
|
|
if (fh->width < 48 ||
|
|
|
|
|
fh->width > NORM_MAXW ||
|
|
|
|
|
fh->height < 32 ||
|
|
|
|
|
fh->height > NORM_MAXH)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
|
|
|
|
|
if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
/* These properties only change when queue is idle, see s_fmt */
|
|
|
|
|
buf->fmt = fh->fmt;
|
|
|
|
|
buf->vb.width = fh->width;
|
|
|
|
|
buf->vb.height = fh->height;
|
|
|
|
|
buf->vb.field = field;
|
|
|
|
|
if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
|
|
|
|
|
rc = videobuf_iolock(vq, &buf->vb, NULL);
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
buf->vb.state = VIDEOBUF_PREPARED;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fail: free_buffer(vq, buf);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_buffer
|
|
|
|
|
*buf = container_of(vb, struct vivi_buffer, vb);
|
|
|
|
|
struct videobuf_res_privdata *res = (struct videobuf_res_privdata *)vq
|
|
|
|
|
->priv_data;
|
|
|
|
|
struct vivi_fh *fh = (struct vivi_fh *)res->priv;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
struct vivi_dmaqueue *vidq = &dev->vidq;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "%s\n", __func__);
|
|
|
|
|
buf->vb.state = VIDEOBUF_QUEUED;
|
|
|
|
|
list_add_tail(&buf->vb.queue, &vidq->active);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void buffer_release(struct videobuf_queue *vq,
|
|
|
|
|
struct videobuf_buffer *vb)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_buffer
|
|
|
|
|
*buf = container_of(vb, struct vivi_buffer, vb);
|
|
|
|
|
free_buffer(vq, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct videobuf_queue_ops vivi_video_qops = {
|
|
|
|
|
.buf_setup = buffer_setup,
|
|
|
|
|
.buf_prepare = buffer_prepare,
|
|
|
|
|
.buf_queue = buffer_queue,
|
|
|
|
|
.buf_release = buffer_release,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
|
* IOCTL vidioc handling
|
|
|
|
|
* ------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
static int vidioc_querycap(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_capability *cap)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
|
|
|
|
|
strcpy(cap->driver, "amlvideo");
|
|
|
|
|
strcpy(cap->card, "amlvideo");
|
|
|
|
|
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
|
|
|
|
|
cap->version = AMLVIDEO_VERSION;
|
|
|
|
|
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
|
|
|
|
|
| V4L2_CAP_READWRITE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_fmtdesc *f)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fmt *fmt;
|
|
|
|
|
|
|
|
|
|
if (f->index >= ARRAY_SIZE(formats))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
fmt = &formats[f->index];
|
|
|
|
|
|
|
|
|
|
strlcpy(f->description, fmt->name, sizeof(f->description));
|
|
|
|
|
f->pixelformat = fmt->fourcc;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_format *f)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
|
|
|
|
|
f->fmt.pix.width = fh->width;
|
|
|
|
|
f->fmt.pix.height = fh->height;
|
|
|
|
|
f->fmt.pix.field = fh->vb_vidq.field;
|
|
|
|
|
f->fmt.pix.pixelformat = fh->fmt->fourcc;
|
|
|
|
|
f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3;
|
|
|
|
|
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_format *f)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
fh->fmt = get_format(f);
|
|
|
|
|
fh->width = f->fmt.pix.width;
|
|
|
|
|
fh->height = f->fmt.pix.height;
|
|
|
|
|
fh->vb_vidq.field = f->fmt.pix.field;
|
|
|
|
|
fh->type = f->type;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*FIXME: This seems to be generic enough to be at videodev2 */
|
|
|
|
|
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_format *f)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
fh->fmt = get_format(f);
|
|
|
|
|
fh->width = f->fmt.pix.width;
|
|
|
|
|
fh->height = f->fmt.pix.height;
|
|
|
|
|
fh->vb_vidq.field = f->fmt.pix.field;
|
|
|
|
|
fh->type = f->type;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_reqbufs(struct file *file, void *priv,
|
|
|
|
|
struct v4l2_requestbuffers *p)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
|
|
|
|
|
return videobuf_reqbufs(&fh->vb_vidq, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
|
|
|
|
|
return videobuf_querybuf(&fh->vb_vidq, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev = video_drvdata(file);
|
|
|
|
|
int ret = 0;
|
|
|
|
|
u64 pts_us64 = 0;
|
|
|
|
|
|
|
|
|
|
if (vfq_level(&dev->q_ready) > AMLVIDEO_POOL_SIZE - 1)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
|
|
if (!vf_peek(dev->vf_receiver_name))
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
|
|
dev->vf = vf_get(dev->vf_receiver_name);
|
|
|
|
|
if (!dev->vf) {
|
|
|
|
|
/* printk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); */
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vfq_push(&dev->q_ready, dev->vf);
|
|
|
|
|
p->index = 0;
|
|
|
|
|
|
|
|
|
|
if (dev->vf->pts_us64) {
|
|
|
|
|
dev->first_frame = 1;
|
|
|
|
|
pts_us64 = dev->vf->pts_us64;
|
|
|
|
|
} else if (dev->first_frame == 0) {
|
|
|
|
|
dev->first_frame = 1;
|
|
|
|
|
pts_us64 = 0;
|
|
|
|
|
} else {
|
|
|
|
|
pts_us64 = dev->last_pts_us64
|
|
|
|
|
+ (DUR2PTS(dev->vf->duration))*100/9;
|
|
|
|
|
}
|
|
|
|
|
p->timestamp.tv_sec = pts_us64 >> 32;
|
|
|
|
|
p->timestamp.tv_usec = pts_us64 & 0xFFFFFFFF;
|
|
|
|
|
dev->last_pts_us64 = pts_us64;
|
|
|
|
|
p->timecode.type = dev->vf->width;
|
|
|
|
|
p->timecode.flags = dev->vf->height;
|
|
|
|
|
|
|
|
|
|
vf_notify_receiver(
|
|
|
|
|
dev->vf_provider_name,
|
|
|
|
|
VFRAME_EVENT_PROVIDER_VFRAME_READY,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_VIDEO_V4L1_COMPAT
|
|
|
|
|
static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
|
|
|
|
|
return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (i != fh->type))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
ret = videobuf_streamon(&fh->vb_vidq);
|
|
|
|
|
if (ret == 0)
|
|
|
|
|
fh->is_streamed_on = 1;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = priv;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (i != fh->type))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
ret = videobuf_streamoff(&fh->vb_vidq);
|
|
|
|
|
if (ret == 0)
|
|
|
|
|
fh->is_streamed_on = 0;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
|
* File operations for the device
|
|
|
|
|
* ------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
/*extern void get_ppmgr_buf_info(char **start, unsigned int *size);*/
|
|
|
|
|
static int amlvideo_open(struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev = video_drvdata(file);
|
|
|
|
|
struct vivi_fh *fh = NULL;
|
|
|
|
|
int retval = 0;
|
|
|
|
|
struct videobuf_res_privdata *res = NULL;
|
|
|
|
|
char *bstart = NULL;
|
|
|
|
|
unsigned int bsize = 0;
|
|
|
|
|
|
|
|
|
|
dev->vf = NULL;
|
|
|
|
|
dev->index = 0;
|
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
dev->users++;
|
|
|
|
|
if (dev->users > 1) {
|
|
|
|
|
dev->users--;
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
res = kzalloc(sizeof(*res), GFP_KERNEL);
|
|
|
|
|
if ((res == NULL) || (dev->res != NULL)) {
|
|
|
|
|
dev->users--;
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
|
|
|
|
if (fh == NULL) {
|
|
|
|
|
kfree(res);
|
|
|
|
|
dev->users--;
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
retval = -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
|
|
|
|
|
file->private_data = fh;
|
|
|
|
|
fh->dev = dev;
|
|
|
|
|
|
|
|
|
|
fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
fh->fmt = &formats[0];
|
|
|
|
|
fh->width = 1920;
|
|
|
|
|
fh->height = 1080;
|
|
|
|
|
|
|
|
|
|
res->priv = (void *)fh;
|
|
|
|
|
dev->res = res;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_ppmgr_buf_info(&bstart, &bsize);
|
|
|
|
|
res->start = (resource_size_t)bstart;
|
|
|
|
|
res->end = (resource_size_t)(bstart + bsize - 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
res->magic = MAGIC_RE_MEM;
|
|
|
|
|
videobuf_queue_res_init(&fh->vb_vidq, &vivi_video_qops, NULL,
|
|
|
|
|
&dev->slock, fh->type, V4L2_FIELD_INTERLACED,
|
|
|
|
|
sizeof(struct vivi_buffer), (void *)res, NULL);
|
|
|
|
|
AMLVIDEO_DBG("amlvideo open");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t amlvideo_read(struct file *file,
|
|
|
|
|
char __user *data, size_t count, loff_t *ppos) {
|
|
|
|
|
struct vivi_fh *fh = file->private_data;
|
|
|
|
|
|
|
|
|
|
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
|
return videobuf_read_stream(
|
|
|
|
|
&fh->vb_vidq, data, count,
|
|
|
|
|
ppos, 0, file->f_flags & O_NONBLOCK);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int amlvideo_poll(struct file *file,
|
|
|
|
|
struct poll_table_struct *wait)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = file->private_data;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
struct videobuf_queue *q = &fh->vb_vidq;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "%s\n", __func__);
|
|
|
|
|
|
|
|
|
|
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
|
return POLLERR;
|
|
|
|
|
|
|
|
|
|
return videobuf_poll_stream(file, q, wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_close(struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = file->private_data;
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
|
|
|
|
|
videobuf_stop(&fh->vb_vidq);
|
|
|
|
|
videobuf_mmap_free(&fh->vb_vidq);
|
|
|
|
|
kfree(fh);
|
|
|
|
|
dev->index = 8;
|
|
|
|
|
/* if (dev->res) { */
|
|
|
|
|
kfree(dev->res);
|
|
|
|
|
dev->res = NULL;
|
|
|
|
|
/* } */
|
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
dev->users--;
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
AMLVIDEO_DBG("amlvideo close");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_fh *fh = file->private_data;
|
|
|
|
|
|
|
|
|
|
struct vivi_dev *dev = fh->dev;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
|
|
|
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
|
|
|
ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
|
|
|
|
|
|
|
|
|
|
dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
|
|
|
|
|
(unsigned long)vma->vm_start,
|
|
|
|
|
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
|
|
|
|
|
ret);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations amlvideo_fops = {
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.open = amlvideo_open,
|
|
|
|
|
.release = amlvideo_close,
|
|
|
|
|
.read = amlvideo_read,
|
|
|
|
|
.poll = amlvideo_poll,
|
|
|
|
|
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
|
|
|
|
|
.mmap = amlvideo_mmap,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops amlvideo_ioctl_ops = {
|
|
|
|
|
.vidioc_querycap = vidioc_querycap,
|
|
|
|
|
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
|
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
|
|
|
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
|
|
|
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
|
|
|
.vidioc_reqbufs = vidioc_reqbufs,
|
|
|
|
|
.vidioc_querybuf = vidioc_querybuf,
|
|
|
|
|
.vidioc_qbuf = vidioc_qbuf,
|
|
|
|
|
.vidioc_dqbuf = vidioc_dqbuf,
|
|
|
|
|
.vidioc_s_std = vidioc_s_std,
|
|
|
|
|
/*.vidioc_queryctrl = vidioc_queryctrl,
|
|
|
|
|
*.vidioc_g_ctrl = vidioc_g_ctrl,
|
|
|
|
|
*.vidioc_s_ctrl = vidioc_s_ctrl,
|
|
|
|
|
*/
|
|
|
|
|
.vidioc_streamon = vidioc_streamon,
|
|
|
|
|
.vidioc_streamoff = vidioc_streamoff,
|
|
|
|
|
#ifdef CONFIG_VIDEO_V4L1_COMPAT
|
|
|
|
|
.vidiocgmbuf = vidiocgmbuf,
|
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct video_device amlvideo_template = {
|
|
|
|
|
.name = "amlvideo",
|
|
|
|
|
.fops = &amlvideo_fops,
|
|
|
|
|
.ioctl_ops = &amlvideo_ioctl_ops,
|
|
|
|
|
.release = video_device_release,
|
|
|
|
|
.tvnorms = V4L2_STD_525_60,
|
|
|
|
|
/* .current_norm = V4L2_STD_NTSC_M , */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* -----------------------------------------------------------------
|
|
|
|
|
* Initialization and module stuff
|
|
|
|
|
* -----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int amlvideo_release(void)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev;
|
|
|
|
|
struct list_head *list;
|
|
|
|
|
|
|
|
|
|
while (!list_empty(&vivi_devlist)) {
|
|
|
|
|
list = vivi_devlist.next;
|
|
|
|
|
list_del(list);
|
|
|
|
|
dev = list_entry(list, struct vivi_dev, vivi_devlist);
|
|
|
|
|
|
|
|
|
|
v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
|
|
|
|
|
video_device_node_name(dev->vfd));
|
|
|
|
|
video_unregister_device(dev->vfd);
|
|
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
|
|
|
|
kfree(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __init amlvideo_create_instance(int inst)
|
|
|
|
|
{
|
|
|
|
|
struct vivi_dev *dev;
|
|
|
|
|
struct video_device *vfd;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
AMLVIDEO_INFO("amlvideo_create_instance called\n");
|
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
|
|
|
if (!dev)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
snprintf(dev->v4l2_dev.name,
|
|
|
|
|
sizeof(dev->v4l2_dev.name),
|
|
|
|
|
"%s-%03d", AVMLVIDEO_MODULE_NAME, inst);
|
|
|
|
|
|
|
|
|
|
AMLVIDEO_INFO("v4l2_dev.name=:%s\n", dev->v4l2_dev.name);
|
|
|
|
|
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
goto free_dev;
|
|
|
|
|
|
|
|
|
|
/* init video dma queues */
|
|
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dev->vidq.active);
|
|
|
|
|
init_waitqueue_head(&dev->vidq.wq);
|
|
|
|
|
|
|
|
|
|
/* initialize locks */
|
|
|
|
|
spin_lock_init(&dev->slock);
|
|
|
|
|
mutex_init(&dev->mutex);
|
|
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
vfd = video_device_alloc();
|
|
|
|
|
|
|
|
|
|
if (!vfd)
|
|
|
|
|
goto unreg_dev;
|
|
|
|
|
|
|
|
|
|
*vfd = amlvideo_template;
|
|
|
|
|
|
|
|
|
|
vfd->dev_debug = debug;
|
|
|
|
|
vfd->v4l2_dev = &dev->v4l2_dev;
|
|
|
|
|
dev->amlvideo_v4l_num = inst * 10 + video_nr_base;
|
|
|
|
|
|
|
|
|
|
/* //////////////////////////////////////// */
|
|
|
|
|
/* vfd->v4l2_dev = &dev->v4l2_dev; */
|
|
|
|
|
/* //////////////////////////////////////// */
|
|
|
|
|
ret = video_register_device(vfd, VFL_TYPE_GRABBER,
|
|
|
|
|
dev->amlvideo_v4l_num);
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto rel_vdev;
|
|
|
|
|
|
|
|
|
|
dev->inst = inst;
|
|
|
|
|
#if 0
|
|
|
|
|
snprintf(dev->vf_receiver_name, AMLVIDEO_VF_NAME_SIZE,
|
|
|
|
|
(0) ? RECEIVER_NAME : RECEIVER_NAME ".%x",
|
|
|
|
|
inst & 0xff);
|
|
|
|
|
|
|
|
|
|
snprintf(dev->vf_provider_name, AMLVIDEO_VF_NAME_SIZE,
|
|
|
|
|
(0) ? PROVIDER_NAME : PROVIDER_NAME ".%x",
|
|
|
|
|
inst & 0xff);
|
|
|
|
|
#else
|
|
|
|
|
memcpy(dev->vf_receiver_name, RECEIVER_NAME, sizeof(RECEIVER_NAME));
|
|
|
|
|
memcpy(dev->vf_provider_name, PROVIDER_NAME, sizeof(PROVIDER_NAME));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
vf_receiver_init(&dev->video_vf_recv,
|
|
|
|
|
dev->vf_receiver_name,
|
|
|
|
|
&video_vf_receiver, dev);
|
|
|
|
|
vf_reg_receiver(&dev->video_vf_recv);
|
|
|
|
|
|
|
|
|
|
video_set_drvdata(vfd, dev);
|
|
|
|
|
|
|
|
|
|
/* Now that everything is fine, let's add it to device list */
|
|
|
|
|
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
|
|
|
|
|
|
|
|
|
|
dev->vfd = vfd;
|
|
|
|
|
|
|
|
|
|
v4l2_info(&dev->v4l2_dev,
|
|
|
|
|
"V4L2 device registered as %s\n",
|
|
|
|
|
video_device_node_name(vfd));
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
rel_vdev: video_device_release(vfd);
|
|
|
|
|
unreg_dev: v4l2_device_unregister(&dev->v4l2_dev);
|
|
|
|
|
free_dev: kfree(dev);
|
|
|
|
|
dev->res = NULL;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_driver_probe(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0, i;
|
|
|
|
|
|
|
|
|
|
AMLVIDEO_INFO("amlvideo_init called");
|
|
|
|
|
if (n_devs <= 0)
|
|
|
|
|
n_devs = 1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_devs; i++) {
|
|
|
|
|
ret = amlvideo_create_instance(i);
|
|
|
|
|
if (ret) {
|
|
|
|
|
/* If some instantiations succeeded,
|
|
|
|
|
* keep driver
|
|
|
|
|
*/
|
|
|
|
|
if (i)
|
|
|
|
|
ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* printk(KERN_INFO "Error %d while
|
|
|
|
|
* loading vivi driver\n", ret);
|
|
|
|
|
*/
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* printk(KERN_INFO "Video Technology Magazine Virtual Video " */
|
|
|
|
|
/* "Capture Board ver %u.%u.%u successfully loaded.\n", */
|
|
|
|
|
/* (AMLVIDEO_VERSION >> 16) & 0xFF, (AMLVIDEO_VERSION >> 8) & 0xFF, */
|
|
|
|
|
/* AMLVIDEO_VERSION & 0xFF); */
|
|
|
|
|
|
|
|
|
|
/* n_devs will reflect the actual number of allocated devices */
|
|
|
|
|
n_devs = i;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amlvideo_drv_remove(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
/*vf_unreg_receiver(&video_vf_recv);*/
|
|
|
|
|
amlvideo_release();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct of_device_id amlvideo_dt_match[] = {
|
|
|
|
|
{
|
|
|
|
|
.compatible = "amlogic, amlvideo",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* general interface for a linux driver .*/
|
|
|
|
|
static struct platform_driver amlvideo_drv = {
|
|
|
|
|
.probe = amlvideo_driver_probe,
|
|
|
|
|
.remove = amlvideo_drv_remove,
|
|
|
|
|
.driver = {
|
|
|
|
|
.name = "amlvideo",
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.of_match_table = amlvideo_dt_match,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#undef NORM_MAXW
|
|
|
|
|
#undef NORM_MAXH
|
|
|
|
|
/* #define __init */
|
|
|
|
|
/* This routine allocates from 1 to n_devs virtual drivers.
|
|
|
|
|
* The real maximum number of virtual drivers will depend on how many drivers
|
|
|
|
|
* will succeed. This is limited to the maximum number of devices that
|
|
|
|
|
* videodev supports, which is equal to VIDEO_NUM_DEVICES.
|
|
|
|
|
*/
|
|
|
|
|
static int __init amlvideo_init(void)
|
|
|
|
|
{
|
|
|
|
|
if (platform_driver_register(&amlvideo_drv)) {
|
|
|
|
|
pr_err("Failed to register amlvideo driver\n");
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __exit amlvideo_exit(void)
|
|
|
|
|
{
|
|
|
|
|
platform_driver_unregister(&amlvideo_drv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_init(amlvideo_init);
|
|
|
|
|
module_exit(amlvideo_exit);
|