1136 lines · 26.4 KB · C
RawDownload
/* * 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/v4l2-ctrls.h>#include <media/v4l2-event.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"); static unsigned debug;module_param(debug, uint, 0644);MODULE_PARM_DESC(debug, "activates debug info"); #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 /* Flags that indicate processing mode */#define MEM2MEM_HFLIP	(1 << 0)#define MEM2MEM_VFLIP	(1 << 1) #define dprintk(dev, fmt, arg...) \	v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)  static 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,	},}; #define NUM_FORMATS ARRAY_SIZE(formats) /* 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_USER_BASE + 0x1000)#define V4L2_CID_TRANS_NUM_BUFS		(V4L2_CID_USER_BASE + 0x1001) 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 v4l2_fh		fh;	struct m2mtest_dev	*dev; 	struct v4l2_ctrl_handler hdl; 	/* 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; 	/* Processing mode */	int			mode; 	enum v4l2_colorspace	colorspace; 	struct v4l2_m2m_ctx	*m2m_ctx; 	/* Source and destination queue data */	struct m2mtest_q_data   q_data[2];}; static inline struct m2mtest_ctx *file2ctx(struct file *file){	return container_of(file->private_data, struct m2mtest_ctx, fh);} static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,					 enum v4l2_buf_type type){	switch (type) {	case V4L2_BUF_TYPE_VIDEO_OUTPUT:		return &ctx->q_data[V4L2_M2M_SRC];	case V4L2_BUF_TYPE_VIDEO_CAPTURE:		return &ctx->q_data[V4L2_M2M_DST];	default:		BUG();	}	return NULL;}  static int device_process(struct m2mtest_ctx *ctx,			  struct vb2_buffer *in_vb,			  struct vb2_buffer *out_vb){	struct m2mtest_dev *dev = ctx->dev;	struct m2mtest_q_data *q_data;	u8 *p_in, *p_out;	int x, y, t, w;	int tile_w, bytes_left;	int width, height, bytesperline; 	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 	width	= q_data->width;	height	= q_data->height;	bytesperline	= (q_data->width * q_data->fmt->depth) >> 3; 	p_in = vb2_plane_vaddr(in_vb, 0);	p_out = vb2_plane_vaddr(out_vb, 0);	if (!p_in || !p_out) {		v4l2_err(&dev->v4l2_dev,			 "Acquiring kernel pointers to buffers failed\n");		return -EFAULT;	} 	if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) {		v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");		return -EINVAL;	} 	tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))		/ MEM2MEM_NUM_TILES;	bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;	w = 0; 	memcpy(&out_vb->v4l2_buf.timestamp,			&in_vb->v4l2_buf.timestamp,			sizeof(struct timeval)); 	switch (ctx->mode) {	case MEM2MEM_HFLIP | MEM2MEM_VFLIP:		p_out += bytesperline * height - bytes_left;		for (y = 0; y < height; ++y) {			for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {				if (w & 0x1) {					for (x = 0; x < tile_w; ++x)						*--p_out = *p_in++ +							MEM2MEM_COLOR_STEP;				} else {					for (x = 0; x < tile_w; ++x)						*--p_out = *p_in++ -							MEM2MEM_COLOR_STEP;				}				++w;			}			p_in += bytes_left;			p_out -= bytes_left;		}		break; 	case MEM2MEM_HFLIP:		for (y = 0; y < height; ++y) {			p_out += MEM2MEM_NUM_TILES * tile_w;			for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {				if (w & 0x01) {					for (x = 0; x < tile_w; ++x)						*--p_out = *p_in++ +							MEM2MEM_COLOR_STEP;				} else {					for (x = 0; x < tile_w; ++x)						*--p_out = *p_in++ -							MEM2MEM_COLOR_STEP;				}				++w;			}			p_in += bytes_left;			p_out += bytesperline;		}		break; 	case MEM2MEM_VFLIP:		p_out += bytesperline * (height - 1);		for (y = 0; y < height; ++y) {			for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {				if (w & 0x1) {					for (x = 0; x < tile_w; ++x)						*p_out++ = *p_in++ +							MEM2MEM_COLOR_STEP;				} else {					for (x = 0; x < tile_w; ++x)						*p_out++ = *p_in++ -							MEM2MEM_COLOR_STEP;				}				++w;			}			p_in += bytes_left;			p_out += bytes_left - 2 * bytesperline;		}		break; 	default:		for (y = 0; y < height; ++y) {			for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {				if (w & 0x1) {					for (x = 0; x < tile_w; ++x)						*p_out++ = *p_in++ +							MEM2MEM_COLOR_STEP;				} else {					for (x = 0; x < tile_w; ++x)						*p_out++ = *p_in++ -							MEM2MEM_COLOR_STEP;				}				++w;			}			p_in += bytes_left;			p_out += bytes_left;		}	} 	return 0;} static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout){	dprintk(dev, "Scheduling a simulated irq\n");	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));} /* * mem2mem callbacks */ /** * job_ready() - check whether an instance is ready to be scheduled to run */static int job_ready(void *priv){	struct m2mtest_ctx *ctx = priv; 	if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen	    || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {		dprintk(ctx->dev, "Not enough buffers available\n");		return 0;	} 	return 1;} static void job_abort(void *priv){	struct m2mtest_ctx *ctx = priv; 	/* Will cancel the transaction in the next interrupt handler */	ctx->aborting = 1;} static void m2mtest_lock(void *priv){	struct m2mtest_ctx *ctx = priv;	struct m2mtest_dev *dev = ctx->dev;	mutex_lock(&dev->dev_mutex);} static void m2mtest_unlock(void *priv){	struct m2mtest_ctx *ctx = priv;	struct m2mtest_dev *dev = ctx->dev;	mutex_unlock(&dev->dev_mutex);}  /* device_run() - prepares and starts the device * * This simulates all the immediate preparations required before starting * a device. This will be called by the framework when it decides to schedule * a particular instance. */static void device_run(void *priv){	struct m2mtest_ctx *ctx = priv;	struct m2mtest_dev *dev = ctx->dev;	struct vb2_buffer *src_buf, *dst_buf; 	src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);	dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); 	device_process(ctx, src_buf, dst_buf); 	/* Run a timer, which simulates a hardware irq  */	schedule_irq(dev, ctx->transtime);} static void device_isr(unsigned long priv){	struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;	struct m2mtest_ctx *curr_ctx;	struct vb2_buffer *src_vb, *dst_vb;	unsigned long flags; 	curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev); 	if (NULL == curr_ctx) {		pr_err("Instance released before the end of transaction\n");		return;	} 	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); 	curr_ctx->num_processed++; 	spin_lock_irqsave(&m2mtest_dev->irqlock, flags);	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);	spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); 	if (curr_ctx->num_processed == curr_ctx->translen	    || curr_ctx->aborting) {		dprintk(curr_ctx->dev, "Finishing transaction\n");		curr_ctx->num_processed = 0;		v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);	} else {		device_run(curr_ctx);	}} /* * video ioctls */static int vidioc_querycap(struct file *file, void *priv,			   struct v4l2_capability *cap){	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);	snprintf(cap->bus_info, sizeof(cap->bus_info),			"platform:%s", MEM2MEM_NAME);	cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;	return 0;} static int enum_fmt(struct v4l2_fmtdesc *f, u32 type){	int i, num;	struct m2mtest_fmt *fmt; 	num = 0; 	for (i = 0; i < NUM_FORMATS; ++i) {		if (formats[i].types & type) {			/* index-th format of type type found ? */			if (num == f->index)				break;			/* Correct type but haven't reached our index yet,			 * just increment per-type index */			++num;		}	} 	if (i < NUM_FORMATS) {		/* Format found */		fmt = &formats[i];		strncpy(f->description, fmt->name, sizeof(f->description) - 1);		f->pixelformat = fmt->fourcc;		return 0;	} 	/* Format not found */	return -EINVAL;} static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,				   struct v4l2_fmtdesc *f){	return enum_fmt(f, MEM2MEM_CAPTURE);} static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,				   struct v4l2_fmtdesc *f){	return enum_fmt(f, MEM2MEM_OUTPUT);} static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f){	struct vb2_queue *vq;	struct m2mtest_q_data *q_data; 	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);	if (!vq)		return -EINVAL; 	q_data = get_q_data(ctx, f->type); 	f->fmt.pix.width	= q_data->width;

Lines 1–500 of 1136.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t