Path: blob/master/drivers/media/video/mem2mem_testdev.c
17296 views
/*1* A virtual v4l2-mem2mem example device.2*3* This is a virtual device driver for testing mem-to-mem videobuf framework.4* It simulates a device that uses memory buffers for both source and5* destination, processes the data and issues an "irq" (simulated by a timer).6* The device is capable of multi-instance, multi-buffer-per-transaction7* operation (via the mem2mem framework).8*9* Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.10* Pawel Osciak, <[email protected]>11* Marek Szyprowski, <[email protected]>12*13* This program is free software; you can redistribute it and/or modify14* it under the terms of the GNU General Public License as published by the15* Free Software Foundation; either version 2 of the16* License, or (at your option) any later version17*/18#include <linux/module.h>19#include <linux/delay.h>20#include <linux/fs.h>21#include <linux/version.h>22#include <linux/timer.h>23#include <linux/sched.h>24#include <linux/slab.h>2526#include <linux/platform_device.h>27#include <media/v4l2-mem2mem.h>28#include <media/v4l2-device.h>29#include <media/v4l2-ioctl.h>30#include <media/videobuf2-vmalloc.h>3132#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"3334MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");35MODULE_AUTHOR("Pawel Osciak, <[email protected]>");36MODULE_LICENSE("GPL");373839#define MIN_W 3240#define MIN_H 3241#define MAX_W 64042#define MAX_H 48043#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */4445/* Flags that indicate a format can be used for capture/output */46#define MEM2MEM_CAPTURE (1 << 0)47#define MEM2MEM_OUTPUT (1 << 1)4849#define MEM2MEM_NAME "m2m-testdev"5051/* Per queue */52#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME53/* In bytes, per queue */54#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)5556/* Default transaction time in msec */57#define MEM2MEM_DEF_TRANSTIME 100058/* Default number of buffers per transaction */59#define MEM2MEM_DEF_TRANSLEN 160#define MEM2MEM_COLOR_STEP (0xff >> 4)61#define MEM2MEM_NUM_TILES 86263#define dprintk(dev, fmt, arg...) \64v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)656667void m2mtest_dev_release(struct device *dev)68{}6970static struct platform_device m2mtest_pdev = {71.name = MEM2MEM_NAME,72.dev.release = m2mtest_dev_release,73};7475struct m2mtest_fmt {76char *name;77u32 fourcc;78int depth;79/* Types the format can be used for */80u32 types;81};8283static struct m2mtest_fmt formats[] = {84{85.name = "RGB565 (BE)",86.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */87.depth = 16,88/* Both capture and output format */89.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,90},91{92.name = "4:2:2, packed, YUYV",93.fourcc = V4L2_PIX_FMT_YUYV,94.depth = 16,95/* Output-only format */96.types = MEM2MEM_OUTPUT,97},98};99100/* Per-queue, driver-specific private data */101struct m2mtest_q_data {102unsigned int width;103unsigned int height;104unsigned int sizeimage;105struct m2mtest_fmt *fmt;106};107108enum {109V4L2_M2M_SRC = 0,110V4L2_M2M_DST = 1,111};112113/* Source and destination queue data */114static struct m2mtest_q_data q_data[2];115116static struct m2mtest_q_data *get_q_data(enum v4l2_buf_type type)117{118switch (type) {119case V4L2_BUF_TYPE_VIDEO_OUTPUT:120return &q_data[V4L2_M2M_SRC];121case V4L2_BUF_TYPE_VIDEO_CAPTURE:122return &q_data[V4L2_M2M_DST];123default:124BUG();125}126return NULL;127}128129#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE130#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1)131132static struct v4l2_queryctrl m2mtest_ctrls[] = {133{134.id = V4L2_CID_TRANS_TIME_MSEC,135.type = V4L2_CTRL_TYPE_INTEGER,136.name = "Transaction time (msec)",137.minimum = 1,138.maximum = 10000,139.step = 100,140.default_value = 1000,141.flags = 0,142}, {143.id = V4L2_CID_TRANS_NUM_BUFS,144.type = V4L2_CTRL_TYPE_INTEGER,145.name = "Buffers per transaction",146.minimum = 1,147.maximum = MEM2MEM_DEF_NUM_BUFS,148.step = 1,149.default_value = 1,150.flags = 0,151},152};153154#define NUM_FORMATS ARRAY_SIZE(formats)155156static struct m2mtest_fmt *find_format(struct v4l2_format *f)157{158struct m2mtest_fmt *fmt;159unsigned int k;160161for (k = 0; k < NUM_FORMATS; k++) {162fmt = &formats[k];163if (fmt->fourcc == f->fmt.pix.pixelformat)164break;165}166167if (k == NUM_FORMATS)168return NULL;169170return &formats[k];171}172173struct m2mtest_dev {174struct v4l2_device v4l2_dev;175struct video_device *vfd;176177atomic_t num_inst;178struct mutex dev_mutex;179spinlock_t irqlock;180181struct timer_list timer;182183struct v4l2_m2m_dev *m2m_dev;184};185186struct m2mtest_ctx {187struct m2mtest_dev *dev;188189/* Processed buffers in this transaction */190u8 num_processed;191192/* Transaction length (i.e. how many buffers per transaction) */193u32 translen;194/* Transaction time (i.e. simulated processing time) in milliseconds */195u32 transtime;196197/* Abort requested by m2m */198int aborting;199200struct v4l2_m2m_ctx *m2m_ctx;201};202203static struct v4l2_queryctrl *get_ctrl(int id)204{205int i;206207for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) {208if (id == m2mtest_ctrls[i].id)209return &m2mtest_ctrls[i];210}211212return NULL;213}214215static int device_process(struct m2mtest_ctx *ctx,216struct vb2_buffer *in_vb,217struct vb2_buffer *out_vb)218{219struct m2mtest_dev *dev = ctx->dev;220struct m2mtest_q_data *q_data;221u8 *p_in, *p_out;222int x, y, t, w;223int tile_w, bytes_left;224int width, height, bytesperline;225226q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);227228width = q_data->width;229height = q_data->height;230bytesperline = (q_data->width * q_data->fmt->depth) >> 3;231232p_in = vb2_plane_vaddr(in_vb, 0);233p_out = vb2_plane_vaddr(out_vb, 0);234if (!p_in || !p_out) {235v4l2_err(&dev->v4l2_dev,236"Acquiring kernel pointers to buffers failed\n");237return -EFAULT;238}239240if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) {241v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");242return -EINVAL;243}244245tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))246/ MEM2MEM_NUM_TILES;247bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;248w = 0;249250for (y = 0; y < height; ++y) {251for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {252if (w & 0x1) {253for (x = 0; x < tile_w; ++x)254*p_out++ = *p_in++ + MEM2MEM_COLOR_STEP;255} else {256for (x = 0; x < tile_w; ++x)257*p_out++ = *p_in++ - MEM2MEM_COLOR_STEP;258}259++w;260}261p_in += bytes_left;262p_out += bytes_left;263}264265return 0;266}267268static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)269{270dprintk(dev, "Scheduling a simulated irq\n");271mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));272}273274/*275* mem2mem callbacks276*/277278/**279* job_ready() - check whether an instance is ready to be scheduled to run280*/281static int job_ready(void *priv)282{283struct m2mtest_ctx *ctx = priv;284285if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen286|| v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {287dprintk(ctx->dev, "Not enough buffers available\n");288return 0;289}290291return 1;292}293294static void job_abort(void *priv)295{296struct m2mtest_ctx *ctx = priv;297298/* Will cancel the transaction in the next interrupt handler */299ctx->aborting = 1;300}301302static void m2mtest_lock(void *priv)303{304struct m2mtest_ctx *ctx = priv;305struct m2mtest_dev *dev = ctx->dev;306mutex_lock(&dev->dev_mutex);307}308309static void m2mtest_unlock(void *priv)310{311struct m2mtest_ctx *ctx = priv;312struct m2mtest_dev *dev = ctx->dev;313mutex_unlock(&dev->dev_mutex);314}315316317/* device_run() - prepares and starts the device318*319* This simulates all the immediate preparations required before starting320* a device. This will be called by the framework when it decides to schedule321* a particular instance.322*/323static void device_run(void *priv)324{325struct m2mtest_ctx *ctx = priv;326struct m2mtest_dev *dev = ctx->dev;327struct vb2_buffer *src_buf, *dst_buf;328329src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);330dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);331332device_process(ctx, src_buf, dst_buf);333334/* Run a timer, which simulates a hardware irq */335schedule_irq(dev, ctx->transtime);336}337338static void device_isr(unsigned long priv)339{340struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;341struct m2mtest_ctx *curr_ctx;342struct vb2_buffer *src_vb, *dst_vb;343unsigned long flags;344345curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);346347if (NULL == curr_ctx) {348printk(KERN_ERR349"Instance released before the end of transaction\n");350return;351}352353src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);354dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);355356curr_ctx->num_processed++;357358spin_lock_irqsave(&m2mtest_dev->irqlock, flags);359v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);360v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);361spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);362363if (curr_ctx->num_processed == curr_ctx->translen364|| curr_ctx->aborting) {365dprintk(curr_ctx->dev, "Finishing transaction\n");366curr_ctx->num_processed = 0;367v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);368} else {369device_run(curr_ctx);370}371}372373/*374* video ioctls375*/376static int vidioc_querycap(struct file *file, void *priv,377struct v4l2_capability *cap)378{379strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);380strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);381cap->bus_info[0] = 0;382cap->version = KERNEL_VERSION(0, 1, 0);383cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT384| V4L2_CAP_STREAMING;385386return 0;387}388389static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)390{391int i, num;392struct m2mtest_fmt *fmt;393394num = 0;395396for (i = 0; i < NUM_FORMATS; ++i) {397if (formats[i].types & type) {398/* index-th format of type type found ? */399if (num == f->index)400break;401/* Correct type but haven't reached our index yet,402* just increment per-type index */403++num;404}405}406407if (i < NUM_FORMATS) {408/* Format found */409fmt = &formats[i];410strncpy(f->description, fmt->name, sizeof(f->description) - 1);411f->pixelformat = fmt->fourcc;412return 0;413}414415/* Format not found */416return -EINVAL;417}418419static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,420struct v4l2_fmtdesc *f)421{422return enum_fmt(f, MEM2MEM_CAPTURE);423}424425static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,426struct v4l2_fmtdesc *f)427{428return enum_fmt(f, MEM2MEM_OUTPUT);429}430431static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)432{433struct vb2_queue *vq;434struct m2mtest_q_data *q_data;435436vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);437if (!vq)438return -EINVAL;439440q_data = get_q_data(f->type);441442f->fmt.pix.width = q_data->width;443f->fmt.pix.height = q_data->height;444f->fmt.pix.field = V4L2_FIELD_NONE;445f->fmt.pix.pixelformat = q_data->fmt->fourcc;446f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;447f->fmt.pix.sizeimage = q_data->sizeimage;448449return 0;450}451452static int vidioc_g_fmt_vid_out(struct file *file, void *priv,453struct v4l2_format *f)454{455return vidioc_g_fmt(priv, f);456}457458static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,459struct v4l2_format *f)460{461return vidioc_g_fmt(priv, f);462}463464static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)465{466enum v4l2_field field;467468field = f->fmt.pix.field;469470if (field == V4L2_FIELD_ANY)471field = V4L2_FIELD_NONE;472else if (V4L2_FIELD_NONE != field)473return -EINVAL;474475/* V4L2 specification suggests the driver corrects the format struct476* if any of the dimensions is unsupported */477f->fmt.pix.field = field;478479if (f->fmt.pix.height < MIN_H)480f->fmt.pix.height = MIN_H;481else if (f->fmt.pix.height > MAX_H)482f->fmt.pix.height = MAX_H;483484if (f->fmt.pix.width < MIN_W)485f->fmt.pix.width = MIN_W;486else if (f->fmt.pix.width > MAX_W)487f->fmt.pix.width = MAX_W;488489f->fmt.pix.width &= ~DIM_ALIGN_MASK;490f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;491f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;492493return 0;494}495496static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,497struct v4l2_format *f)498{499struct m2mtest_fmt *fmt;500struct m2mtest_ctx *ctx = priv;501502fmt = find_format(f);503if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {504v4l2_err(&ctx->dev->v4l2_dev,505"Fourcc format (0x%08x) invalid.\n",506f->fmt.pix.pixelformat);507return -EINVAL;508}509510return vidioc_try_fmt(f, fmt);511}512513static int vidioc_try_fmt_vid_out(struct file *file, void *priv,514struct v4l2_format *f)515{516struct m2mtest_fmt *fmt;517struct m2mtest_ctx *ctx = priv;518519fmt = find_format(f);520if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {521v4l2_err(&ctx->dev->v4l2_dev,522"Fourcc format (0x%08x) invalid.\n",523f->fmt.pix.pixelformat);524return -EINVAL;525}526527return vidioc_try_fmt(f, fmt);528}529530static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)531{532struct m2mtest_q_data *q_data;533struct vb2_queue *vq;534535vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);536if (!vq)537return -EINVAL;538539q_data = get_q_data(f->type);540if (!q_data)541return -EINVAL;542543if (vb2_is_busy(vq)) {544v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);545return -EBUSY;546}547548q_data->fmt = find_format(f);549q_data->width = f->fmt.pix.width;550q_data->height = f->fmt.pix.height;551q_data->sizeimage = q_data->width * q_data->height552* q_data->fmt->depth >> 3;553554dprintk(ctx->dev,555"Setting format for type %d, wxh: %dx%d, fmt: %d\n",556f->type, q_data->width, q_data->height, q_data->fmt->fourcc);557558return 0;559}560561static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,562struct v4l2_format *f)563{564int ret;565566ret = vidioc_try_fmt_vid_cap(file, priv, f);567if (ret)568return ret;569570return vidioc_s_fmt(priv, f);571}572573static int vidioc_s_fmt_vid_out(struct file *file, void *priv,574struct v4l2_format *f)575{576int ret;577578ret = vidioc_try_fmt_vid_out(file, priv, f);579if (ret)580return ret;581582return vidioc_s_fmt(priv, f);583}584585static int vidioc_reqbufs(struct file *file, void *priv,586struct v4l2_requestbuffers *reqbufs)587{588struct m2mtest_ctx *ctx = priv;589590return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);591}592593static int vidioc_querybuf(struct file *file, void *priv,594struct v4l2_buffer *buf)595{596struct m2mtest_ctx *ctx = priv;597598return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);599}600601static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)602{603struct m2mtest_ctx *ctx = priv;604605return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);606}607608static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)609{610struct m2mtest_ctx *ctx = priv;611612return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);613}614615static int vidioc_streamon(struct file *file, void *priv,616enum v4l2_buf_type type)617{618struct m2mtest_ctx *ctx = priv;619620return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);621}622623static int vidioc_streamoff(struct file *file, void *priv,624enum v4l2_buf_type type)625{626struct m2mtest_ctx *ctx = priv;627628return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);629}630631static int vidioc_queryctrl(struct file *file, void *priv,632struct v4l2_queryctrl *qc)633{634struct v4l2_queryctrl *c;635636c = get_ctrl(qc->id);637if (!c)638return -EINVAL;639640*qc = *c;641return 0;642}643644static int vidioc_g_ctrl(struct file *file, void *priv,645struct v4l2_control *ctrl)646{647struct m2mtest_ctx *ctx = priv;648649switch (ctrl->id) {650case V4L2_CID_TRANS_TIME_MSEC:651ctrl->value = ctx->transtime;652break;653654case V4L2_CID_TRANS_NUM_BUFS:655ctrl->value = ctx->translen;656break;657658default:659v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");660return -EINVAL;661}662663return 0;664}665666static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl)667{668struct v4l2_queryctrl *c;669670c = get_ctrl(ctrl->id);671if (!c)672return -EINVAL;673674if (ctrl->value < c->minimum || ctrl->value > c->maximum) {675v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n");676return -ERANGE;677}678679return 0;680}681682static int vidioc_s_ctrl(struct file *file, void *priv,683struct v4l2_control *ctrl)684{685struct m2mtest_ctx *ctx = priv;686int ret = 0;687688ret = check_ctrl_val(ctx, ctrl);689if (ret != 0)690return ret;691692switch (ctrl->id) {693case V4L2_CID_TRANS_TIME_MSEC:694ctx->transtime = ctrl->value;695break;696697case V4L2_CID_TRANS_NUM_BUFS:698ctx->translen = ctrl->value;699break;700701default:702v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");703return -EINVAL;704}705706return 0;707}708709710static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {711.vidioc_querycap = vidioc_querycap,712713.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,714.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,715.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,716.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,717718.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,719.vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,720.vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,721.vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,722723.vidioc_reqbufs = vidioc_reqbufs,724.vidioc_querybuf = vidioc_querybuf,725726.vidioc_qbuf = vidioc_qbuf,727.vidioc_dqbuf = vidioc_dqbuf,728729.vidioc_streamon = vidioc_streamon,730.vidioc_streamoff = vidioc_streamoff,731732.vidioc_queryctrl = vidioc_queryctrl,733.vidioc_g_ctrl = vidioc_g_ctrl,734.vidioc_s_ctrl = vidioc_s_ctrl,735};736737738/*739* Queue operations740*/741742static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,743unsigned int *nplanes, unsigned long sizes[],744void *alloc_ctxs[])745{746struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);747struct m2mtest_q_data *q_data;748unsigned int size, count = *nbuffers;749750q_data = get_q_data(vq->type);751752size = q_data->width * q_data->height * q_data->fmt->depth >> 3;753754while (size * count > MEM2MEM_VID_MEM_LIMIT)755(count)--;756757*nplanes = 1;758*nbuffers = count;759sizes[0] = size;760761/*762* videobuf2-vmalloc allocator is context-less so no need to set763* alloc_ctxs array.764*/765766dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);767768return 0;769}770771static int m2mtest_buf_prepare(struct vb2_buffer *vb)772{773struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);774struct m2mtest_q_data *q_data;775776dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);777778q_data = get_q_data(vb->vb2_queue->type);779780if (vb2_plane_size(vb, 0) < q_data->sizeimage) {781dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",782__func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);783return -EINVAL;784}785786vb2_set_plane_payload(vb, 0, q_data->sizeimage);787788return 0;789}790791static void m2mtest_buf_queue(struct vb2_buffer *vb)792{793struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);794v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);795}796797static struct vb2_ops m2mtest_qops = {798.queue_setup = m2mtest_queue_setup,799.buf_prepare = m2mtest_buf_prepare,800.buf_queue = m2mtest_buf_queue,801};802803static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)804{805struct m2mtest_ctx *ctx = priv;806int ret;807808memset(src_vq, 0, sizeof(*src_vq));809src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;810src_vq->io_modes = VB2_MMAP;811src_vq->drv_priv = ctx;812src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);813src_vq->ops = &m2mtest_qops;814src_vq->mem_ops = &vb2_vmalloc_memops;815816ret = vb2_queue_init(src_vq);817if (ret)818return ret;819820memset(dst_vq, 0, sizeof(*dst_vq));821dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;822dst_vq->io_modes = VB2_MMAP;823dst_vq->drv_priv = ctx;824dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);825dst_vq->ops = &m2mtest_qops;826dst_vq->mem_ops = &vb2_vmalloc_memops;827828return vb2_queue_init(dst_vq);829}830831/*832* File operations833*/834static int m2mtest_open(struct file *file)835{836struct m2mtest_dev *dev = video_drvdata(file);837struct m2mtest_ctx *ctx = NULL;838839ctx = kzalloc(sizeof *ctx, GFP_KERNEL);840if (!ctx)841return -ENOMEM;842843file->private_data = ctx;844ctx->dev = dev;845ctx->translen = MEM2MEM_DEF_TRANSLEN;846ctx->transtime = MEM2MEM_DEF_TRANSTIME;847ctx->num_processed = 0;848849ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);850851if (IS_ERR(ctx->m2m_ctx)) {852int ret = PTR_ERR(ctx->m2m_ctx);853854kfree(ctx);855return ret;856}857858atomic_inc(&dev->num_inst);859860dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);861862return 0;863}864865static int m2mtest_release(struct file *file)866{867struct m2mtest_dev *dev = video_drvdata(file);868struct m2mtest_ctx *ctx = file->private_data;869870dprintk(dev, "Releasing instance %p\n", ctx);871872v4l2_m2m_ctx_release(ctx->m2m_ctx);873kfree(ctx);874875atomic_dec(&dev->num_inst);876877return 0;878}879880static unsigned int m2mtest_poll(struct file *file,881struct poll_table_struct *wait)882{883struct m2mtest_ctx *ctx = file->private_data;884885return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);886}887888static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)889{890struct m2mtest_ctx *ctx = file->private_data;891892return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);893}894895static const struct v4l2_file_operations m2mtest_fops = {896.owner = THIS_MODULE,897.open = m2mtest_open,898.release = m2mtest_release,899.poll = m2mtest_poll,900.unlocked_ioctl = video_ioctl2,901.mmap = m2mtest_mmap,902};903904static struct video_device m2mtest_videodev = {905.name = MEM2MEM_NAME,906.fops = &m2mtest_fops,907.ioctl_ops = &m2mtest_ioctl_ops,908.minor = -1,909.release = video_device_release,910};911912static struct v4l2_m2m_ops m2m_ops = {913.device_run = device_run,914.job_ready = job_ready,915.job_abort = job_abort,916.lock = m2mtest_lock,917.unlock = m2mtest_unlock,918};919920static int m2mtest_probe(struct platform_device *pdev)921{922struct m2mtest_dev *dev;923struct video_device *vfd;924int ret;925926dev = kzalloc(sizeof *dev, GFP_KERNEL);927if (!dev)928return -ENOMEM;929930spin_lock_init(&dev->irqlock);931932ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);933if (ret)934goto free_dev;935936atomic_set(&dev->num_inst, 0);937mutex_init(&dev->dev_mutex);938939vfd = video_device_alloc();940if (!vfd) {941v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");942ret = -ENOMEM;943goto unreg_dev;944}945946*vfd = m2mtest_videodev;947vfd->lock = &dev->dev_mutex;948949ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);950if (ret) {951v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");952goto rel_vdev;953}954955video_set_drvdata(vfd, dev);956snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);957dev->vfd = vfd;958v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME959"Device registered as /dev/video%d\n", vfd->num);960961setup_timer(&dev->timer, device_isr, (long)dev);962platform_set_drvdata(pdev, dev);963964dev->m2m_dev = v4l2_m2m_init(&m2m_ops);965if (IS_ERR(dev->m2m_dev)) {966v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");967ret = PTR_ERR(dev->m2m_dev);968goto err_m2m;969}970971q_data[V4L2_M2M_SRC].fmt = &formats[0];972q_data[V4L2_M2M_DST].fmt = &formats[0];973974return 0;975976v4l2_m2m_release(dev->m2m_dev);977err_m2m:978video_unregister_device(dev->vfd);979rel_vdev:980video_device_release(vfd);981unreg_dev:982v4l2_device_unregister(&dev->v4l2_dev);983free_dev:984kfree(dev);985986return ret;987}988989static int m2mtest_remove(struct platform_device *pdev)990{991struct m2mtest_dev *dev =992(struct m2mtest_dev *)platform_get_drvdata(pdev);993994v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);995v4l2_m2m_release(dev->m2m_dev);996del_timer_sync(&dev->timer);997video_unregister_device(dev->vfd);998v4l2_device_unregister(&dev->v4l2_dev);999kfree(dev);10001001return 0;1002}10031004static struct platform_driver m2mtest_pdrv = {1005.probe = m2mtest_probe,1006.remove = m2mtest_remove,1007.driver = {1008.name = MEM2MEM_NAME,1009.owner = THIS_MODULE,1010},1011};10121013static void __exit m2mtest_exit(void)1014{1015platform_driver_unregister(&m2mtest_pdrv);1016platform_device_unregister(&m2mtest_pdev);1017}10181019static int __init m2mtest_init(void)1020{1021int ret;10221023ret = platform_device_register(&m2mtest_pdev);1024if (ret)1025return ret;10261027ret = platform_driver_register(&m2mtest_pdrv);1028if (ret)1029platform_device_unregister(&m2mtest_pdev);10301031return 0;1032}10331034module_init(m2mtest_init);1035module_exit(m2mtest_exit);1036103710381039