/*
* A virtual v4l2-mem2mem example device.
*
* This is a virtual device driver for testing mem-to-mem videobuf framework.
* It simulates a device that uses memory buffers for both source and
* destination, processes the data and issues an "irq" (simulated by a timer).
* The device is capable of multi-instance, multi-buffer-per-transaction
* operation (via the mem2mem framework).
*
* Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
* Pawel Osciak, <pawel@osciak.com>
* Marek Szyprowski, <m.szyprowski@samsung.com>
*
* 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
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-vmalloc.h>
#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.1");
#define MIN_W 32
#define MIN_H 32
#define MAX_W 640
#define MAX_H 480
#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
/* Flags that indicate a format can be used for capture/output */
#define MEM2MEM_CAPTURE (1 << 0)
#define MEM2MEM_OUTPUT (1 << 1)
#define MEM2MEM_NAME "m2m-testdev"
/* Per queue */
#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
/* In bytes, per queue */
#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
/* Default transaction time in msec */
#define MEM2MEM_DEF_TRANSTIME 1000
/* Default number of buffers per transaction */
#define MEM2MEM_DEF_TRANSLEN 1
#define MEM2MEM_COLOR_STEP (0xff >> 4)
#define MEM2MEM_NUM_TILES 8
#define dprintk(dev, fmt, arg...) \
v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
void m2mtest_dev_release(struct device *dev)
{}
static struct platform_device m2mtest_pdev = {
.name = MEM2MEM_NAME,
.dev.release = m2mtest_dev_release,
};
struct m2mtest_fmt {
char *name;
u32 fourcc;
int depth;
/* Types the format can be used for */
u32 types;
};
static struct m2mtest_fmt formats[] = {
{
.name = "RGB565 (BE)",
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
.depth = 16,
/* Both capture and output format */
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
},
{
.name = "4:2:2, packed, YUYV",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
/* Output-only format */
.types = MEM2MEM_OUTPUT,
},
};
/* Per-queue, driver-specific private data */
struct m2mtest_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage;
struct m2mtest_fmt *fmt;
};
enum {
V4L2_M2M_SRC = 0,
V4L2_M2M_DST = 1,
};
#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE
#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1)
static struct v4l2_queryctrl m2mtest_ctrls[] = {
{
.id = V4L2_CID_TRANS_TIME_MSEC,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Transaction time (msec)",
.minimum = 1,
.maximum = 10000,
.step = 100,
.default_value = 1000,
.flags = 0,
}, {
.id = V4L2_CID_TRANS_NUM_BUFS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Buffers per transaction",
.minimum = 1,
.maximum = MEM2MEM_DEF_NUM_BUFS,
.step = 1,
.default_value = 1,
.flags = 0,
},
};
#define NUM_FORMATS ARRAY_SIZE(formats)
static struct m2mtest_fmt *find_format(struct v4l2_format *f)
{
struct m2mtest_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &formats[k];
if (fmt->fourcc == f->fmt.pix.pixelformat)
break;
}
if (k == NUM_FORMATS)
return NULL;
return &formats[k];
}
struct m2mtest_dev {
struct v4l2_device v4l2_dev;
struct video_device *vfd;
atomic_t num_inst;
struct mutex dev_mutex;
spinlock_t irqlock;
struct timer_list timer;
struct v4l2_m2m_dev *m2m_dev;
};
struct m2mtest_ctx {
struct m2mtest_dev *dev;
/* Processed buffers in this transaction */
u8 num_processed;
/* Transaction length (i.e. how many buffers per transaction) */
u32 translen;
/* Transaction time (i.e. simulated processing time) in milliseconds */
u32 transtime;
/* Abort requested by m2m */
int aborting;
struct v4l2_m2m_ctx *m2m_ctx;
/* Source and destination queue data */
struct m2mtest_q_data q_data[2];
};
static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
enum v4l2_buf_type type)
{
switch (type) {