aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/vim2m.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/vim2m.c')
-rw-r--r--drivers/media/platform/vim2m.c675
1 files changed, 476 insertions, 199 deletions
diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index 89d9c4c21037..34dcaca45d8b 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* A virtual v4l2-mem2mem example device.
*
@@ -34,22 +35,34 @@
MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.1.1");
+MODULE_VERSION("0.2");
MODULE_ALIAS("mem2mem_testdev");
-static unsigned debug;
+static unsigned int debug;
module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
+MODULE_PARM_DESC(debug, "debug level");
+
+/* Default transaction time in msec */
+static unsigned int default_transtime = 40; /* Max 25 fps */
+module_param(default_transtime, uint, 0644);
+MODULE_PARM_DESC(default_transtime, "default transaction time in ms");
#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 */
+
+/* Pixel alignment for non-bayer formats */
+#define WIDTH_ALIGN 2
+#define HEIGHT_ALIGN 1
+
+/* Pixel alignment for bayer formats */
+#define BAYER_WIDTH_ALIGN 2
+#define BAYER_HEIGHT_ALIGN 2
/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
+#define MEM2MEM_CAPTURE BIT(0)
+#define MEM2MEM_OUTPUT BIT(1)
#define MEM2MEM_NAME "vim2m"
@@ -58,18 +71,12 @@ MODULE_PARM_DESC(debug, "activates debug info");
/* In bytes, per queue */
#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
-/* Default transaction time in msec */
-#define MEM2MEM_DEF_TRANSTIME 40
-#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)
+#define MEM2MEM_HFLIP BIT(0)
+#define MEM2MEM_VFLIP BIT(1)
+#define dprintk(dev, lvl, fmt, arg...) \
+ v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
static void vim2m_dev_release(struct device *dev)
{}
@@ -83,21 +90,46 @@ struct vim2m_fmt {
u32 fourcc;
int depth;
/* Types the format can be used for */
- u32 types;
+ u32 types;
};
static struct vim2m_fmt formats[] = {
{
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */
.depth = 16,
- /* Both capture and output format */
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- },
- {
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
- /* Output-only format */
- .types = MEM2MEM_OUTPUT,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
},
};
@@ -120,14 +152,14 @@ enum {
#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
-static struct vim2m_fmt *find_format(struct v4l2_format *f)
+static struct vim2m_fmt *find_format(u32 fourcc)
{
struct vim2m_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
+ if (fmt->fourcc == fourcc)
break;
}
@@ -137,6 +169,24 @@ static struct vim2m_fmt *find_format(struct v4l2_format *f)
return &formats[k];
}
+static void get_alignment(u32 fourcc,
+ unsigned int *walign, unsigned int *halign)
+{
+ switch (fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ *walign = BAYER_WIDTH_ALIGN;
+ *halign = BAYER_HEIGHT_ALIGN;
+ return;
+ default:
+ *walign = WIDTH_ALIGN;
+ *halign = HEIGHT_ALIGN;
+ return;
+ }
+}
+
struct vim2m_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd;
@@ -146,9 +196,6 @@ struct vim2m_dev {
atomic_t num_inst;
struct mutex dev_mutex;
- spinlock_t irqlock;
-
- struct delayed_work work_run;
struct v4l2_m2m_dev *m2m_dev;
};
@@ -167,6 +214,10 @@ struct vim2m_ctx {
/* Transaction time (i.e. simulated processing time) in milliseconds */
u32 transtime;
+ struct mutex vb_mutex;
+ struct delayed_work work_run;
+ spinlock_t irqlock;
+
/* Abort requested by m2m */
int aborting;
@@ -188,7 +239,7 @@ static inline struct vim2m_ctx *file2ctx(struct file *file)
}
static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
- enum v4l2_buf_type type)
+ enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -196,28 +247,221 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &ctx->q_data[V4L2_M2M_DST];
default:
- BUG();
+ return NULL;
+ }
+}
+
+static const char *type_name(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return "Output";
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return "Capture";
+ default:
+ return "Invalid";
+ }
+}
+
+#define CLIP(__color) \
+ (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color)))
+
+static void copy_line(struct vim2m_q_data *q_data_out,
+ u8 *src, u8 *dst, bool reverse)
+{
+ int x, depth = q_data_out->fmt->depth >> 3;
+
+ if (!reverse) {
+ memcpy(dst, src, q_data_out->width * depth);
+ } else {
+ for (x = 0; x < q_data_out->width >> 1; x++) {
+ memcpy(dst, src, depth);
+ memcpy(dst + depth, src - depth, depth);
+ src -= depth << 1;
+ dst += depth << 1;
+ }
+ return;
}
- return NULL;
}
+static void copy_two_pixels(struct vim2m_q_data *q_data_in,
+ struct vim2m_q_data *q_data_out,
+ u8 *src[2], u8 **dst, int ypos, bool reverse)
+{
+ struct vim2m_fmt *out = q_data_out->fmt;
+ struct vim2m_fmt *in = q_data_in->fmt;
+ u8 _r[2], _g[2], _b[2], *r, *g, *b;
+ int i;
+
+ /* Step 1: read two consecutive pixels from src pointer */
+
+ r = _r;
+ g = _g;
+ b = _b;
+
+ switch (in->fourcc) {
+ case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
+ for (i = 0; i < 2; i++) {
+ u16 pix = *(u16 *)(src[i]);
+
+ *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07;
+ *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03;
+ *b++ = (u8)((pix & 0x1f) << 3) | 0x07;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
+ for (i = 0; i < 2; i++) {
+ u16 pix = *(u16 *)(src[i]);
+
+ *r++ = (u8)(((0x00f8 & pix) >> 3) << 3) | 0x07;
+ *g++ = (u8)(((pix & 0x7) << 2) |
+ ((pix & 0xe000) >> 5)) | 0x03;
+ *b++ = (u8)(((pix & 0x1f00) >> 8) << 3) | 0x07;
+ }
+ break;
+ default:
+ case V4L2_PIX_FMT_RGB24:
+ for (i = 0; i < 2; i++) {
+ *r++ = src[i][0];
+ *g++ = src[i][1];
+ *b++ = src[i][2];
+ }
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ for (i = 0; i < 2; i++) {
+ *b++ = src[i][0];
+ *g++ = src[i][1];
+ *r++ = src[i][2];
+ }
+ break;
+ }
+
+ /* Step 2: store two consecutive points, reversing them if needed */
+
+ r = _r;
+ g = _g;
+ b = _b;
+
+ switch (out->fourcc) {
+ case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
+ for (i = 0; i < 2; i++) {
+ u16 *pix = (u16 *)*dst;
+
+ *pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) |
+ (*b >> 3);
+
+ *dst += 2;
+ }
+ return;
+ case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
+ for (i = 0; i < 2; i++) {
+ u16 *pix = (u16 *)*dst;
+ u8 green = *g++ >> 2;
+
+ *pix = ((green << 8) & 0xe000) | (green & 0x07) |
+ ((*b++ << 5) & 0x1f00) | ((*r++ & 0xf8));
+
+ *dst += 2;
+ }
+ return;
+ case V4L2_PIX_FMT_RGB24:
+ for (i = 0; i < 2; i++) {
+ *(*dst)++ = *r++;
+ *(*dst)++ = *g++;
+ *(*dst)++ = *b++;
+ }
+ return;
+ case V4L2_PIX_FMT_BGR24:
+ for (i = 0; i < 2; i++) {
+ *(*dst)++ = *b++;
+ *(*dst)++ = *g++;
+ *(*dst)++ = *r++;
+ }
+ return;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ {
+ u8 y, y1, u, v;
+
+ y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
+ + 524288) >> 15);
+ u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b)
+ + 4210688) >> 15);
+ v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++)
+ + 4210688) >> 15);
+ y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
+ + 524288) >> 15);
+
+ *(*dst)++ = y;
+ *(*dst)++ = u;
+
+ *(*dst)++ = y1;
+ *(*dst)++ = v;
+ return;
+ }
+ case V4L2_PIX_FMT_SBGGR8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *b;
+ *(*dst)++ = *++g;
+ } else {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++r;
+ }
+ return;
+ case V4L2_PIX_FMT_SGBRG8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++b;
+ } else {
+ *(*dst)++ = *r;
+ *(*dst)++ = *++g;
+ }
+ return;
+ case V4L2_PIX_FMT_SGRBG8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++r;
+ } else {
+ *(*dst)++ = *b;
+ *(*dst)++ = *++g;
+ }
+ return;
+ case V4L2_PIX_FMT_SRGGB8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *r;
+ *(*dst)++ = *++g;
+ } else {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++b;
+ }
+ return;
+ }
+}
static int device_process(struct vim2m_ctx *ctx,
struct vb2_v4l2_buffer *in_vb,
struct vb2_v4l2_buffer *out_vb)
{
struct vim2m_dev *dev = ctx->dev;
- struct vim2m_q_data *q_data;
- u8 *p_in, *p_out;
- int x, y, t, w;
- int tile_w, bytes_left;
- int width, height, bytesperline;
+ struct vim2m_q_data *q_data_in, *q_data_out;
+ u8 *p_in, *p_line, *p_in_x[2], *p, *p_out;
+ unsigned int width, height, bytesperline, bytes_per_pixel;
+ unsigned int x, y, y_in, y_out, x_int, x_fract, x_err, x_offset;
+ int start, end, step;
+
+ q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (!q_data_in)
+ return 0;
+ bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3;
+ bytes_per_pixel = q_data_in->fmt->depth >> 3;
- q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!q_data_out)
+ return 0;
- width = q_data->width;
- height = q_data->height;
- bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
+ /* As we're doing scaling, use the output dimensions here */
+ height = q_data_out->height;
+ width = q_data_out->width;
p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0);
p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0);
@@ -227,109 +471,86 @@ static int device_process(struct vim2m_ctx *ctx,
return -EFAULT;
}
- if (vb2_plane_size(&in_vb->vb2_buf, 0) >
- vb2_plane_size(&out_vb->vb2_buf, 0)) {
- v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
- return -EINVAL;
- }
+ out_vb->sequence = q_data_out->sequence++;
+ in_vb->sequence = q_data_in->sequence++;
+ v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true);
- tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
- / MEM2MEM_NUM_TILES;
- bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
- w = 0;
-
- out_vb->sequence =
- get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
- in_vb->sequence = q_data->sequence++;
- out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;
-
- if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
- out_vb->timecode = in_vb->timecode;
- out_vb->field = in_vb->field;
- out_vb->flags = in_vb->flags &
- (V4L2_BUF_FLAG_TIMECODE |
- V4L2_BUF_FLAG_KEYFRAME |
- V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME |
- V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
-
- 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;
+ if (ctx->mode & MEM2MEM_VFLIP) {
+ start = height - 1;
+ end = -1;
+ step = -1;
+ } else {
+ start = 0;
+ end = height;
+ step = 1;
+ }
+ y_out = 0;
+
+ /*
+ * When format and resolution are identical,
+ * we can use a faster copy logic
+ */
+ if (q_data_in->fmt->fourcc == q_data_out->fmt->fourcc &&
+ q_data_in->width == q_data_out->width &&
+ q_data_in->height == q_data_out->height) {
+ for (y = start; y != end; y += step, y_out++) {
+ p = p_in + (y * bytesperline);
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p += bytesperline - (q_data_in->fmt->depth >> 3);
+
+ copy_line(q_data_out, p, p_out,
+ ctx->mode & MEM2MEM_HFLIP);
- 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;
+ return 0;
+ }
+
+ /* Slower algorithm with format conversion, hflip, vflip and scaler */
+
+ /* To speed scaler up, use Bresenham for X dimension */
+ x_int = q_data_in->width / q_data_out->width;
+ x_fract = q_data_in->width % q_data_out->width;
+
+ for (y = start; y != end; y += step, y_out++) {
+ y_in = (y * q_data_in->height) / q_data_out->height;
+ x_offset = 0;
+ x_err = 0;
- 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_line = p_in + (y_in * bytesperline);
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_line += bytesperline - (q_data_in->fmt->depth >> 3);
+ p_in_x[0] = p_line;
+
+ for (x = 0; x < width >> 1; x++) {
+ x_offset += x_int;
+ x_err += x_fract;
+ if (x_err > width) {
+ x_offset++;
+ x_err -= width;
}
- 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;
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_in_x[1] = p_line - x_offset * bytes_per_pixel;
+ else
+ p_in_x[1] = p_line + x_offset * bytes_per_pixel;
+
+ copy_two_pixels(q_data_in, q_data_out,
+ p_in_x, &p_out, y_out,
+ ctx->mode & MEM2MEM_HFLIP);
+
+ /* Calculate the next p_in_x0 */
+ x_offset += x_int;
+ x_err += x_fract;
+ if (x_err > width) {
+ x_offset++;
+ x_err -= width;
}
- p_in += bytes_left;
- p_out += bytes_left;
+
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_in_x[0] = p_line - x_offset * bytes_per_pixel;
+ else
+ p_in_x[0] = p_line + x_offset * bytes_per_pixel;
}
}
@@ -349,7 +570,7 @@ static int job_ready(void *priv)
if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
|| v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
- dprintk(ctx->dev, "Not enough buffers available\n");
+ dprintk(ctx->dev, 1, "Not enough buffers available\n");
return 0;
}
@@ -373,7 +594,6 @@ static void job_abort(void *priv)
static void device_run(void *priv)
{
struct vim2m_ctx *ctx = priv;
- struct vim2m_dev *dev = ctx->dev;
struct vb2_v4l2_buffer *src_buf, *dst_buf;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
@@ -390,37 +610,38 @@ static void device_run(void *priv)
&ctx->hdl);
/* Run delayed work, which simulates a hardware irq */
- schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
+ schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime));
}
static void device_work(struct work_struct *w)
{
- struct vim2m_dev *vim2m_dev =
- container_of(w, struct vim2m_dev, work_run.work);
struct vim2m_ctx *curr_ctx;
+ struct vim2m_dev *vim2m_dev;
struct vb2_v4l2_buffer *src_vb, *dst_vb;
unsigned long flags;
- curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev);
+ curr_ctx = container_of(w, struct vim2m_ctx, work_run.work);
- if (NULL == curr_ctx) {
+ if (!curr_ctx) {
pr_err("Instance released before the end of transaction\n");
return;
}
+ vim2m_dev = curr_ctx->dev;
+
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
curr_ctx->num_processed++;
- spin_lock_irqsave(&vim2m_dev->irqlock, flags);
+ spin_lock_irqsave(&curr_ctx->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(&vim2m_dev->irqlock, flags);
+ spin_unlock_irqrestore(&curr_ctx->irqlock, flags);
if (curr_ctx->num_processed == curr_ctx->translen
|| curr_ctx->aborting) {
- dprintk(curr_ctx->dev, "Finishing transaction\n");
+ dprintk(curr_ctx->dev, 2, "Finishing capture buffer fill\n");
curr_ctx->num_processed = 0;
v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
} else {
@@ -437,7 +658,7 @@ static int vidioc_querycap(struct file *file, void *priv,
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);
+ "platform:%s", MEM2MEM_NAME);
return 0;
}
@@ -453,8 +674,10 @@ static int enum_fmt(struct v4l2_fmtdesc *f, u32 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 */
+ /*
+ * Correct type but haven't reached our index yet,
+ * just increment per-type index
+ */
++num;
}
}
@@ -482,6 +705,27 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
return enum_fmt(f, MEM2MEM_OUTPUT);
}
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ if (!find_format(fsize->pixel_format))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = MIN_W;
+ fsize->stepwise.min_height = MIN_H;
+ fsize->stepwise.max_width = MAX_W;
+ fsize->stepwise.max_height = MAX_H;
+
+ get_alignment(fsize->pixel_format,
+ &fsize->stepwise.step_width,
+ &fsize->stepwise.step_height);
+ return 0;
+}
+
static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
struct vb2_queue *vq;
@@ -492,6 +736,8 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
return -EINVAL;
q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
@@ -521,8 +767,11 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
{
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
+ int walign, halign;
+ /*
+ * V4L2 specification specifies the driver corrects the
+ * format struct if any of the dimensions is unsupported
+ */
if (f->fmt.pix.height < MIN_H)
f->fmt.pix.height = MIN_H;
else if (f->fmt.pix.height > MAX_H)
@@ -533,7 +782,9 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
else if (f->fmt.pix.width > MAX_W)
f->fmt.pix.width = MAX_W;
- f->fmt.pix.width &= ~DIM_ALIGN_MASK;
+ get_alignment(f->fmt.pix.pixelformat, &walign, &halign);
+ f->fmt.pix.width &= ~(walign - 1);
+ f->fmt.pix.height &= ~(halign - 1);
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.field = V4L2_FIELD_NONE;
@@ -547,10 +798,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct vim2m_fmt *fmt;
struct vim2m_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -572,10 +823,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct vim2m_fmt *fmt;
struct vim2m_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -607,15 +858,20 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
return -EBUSY;
}
- q_data->fmt = find_format(f);
+ q_data->fmt = find_format(f->fmt.pix.pixelformat);
q_data->width = f->fmt.pix.width;
q_data->height = f->fmt.pix.height;
q_data->sizeimage = q_data->width * q_data->height
* q_data->fmt->depth >> 3;
- dprintk(ctx->dev,
- "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+ dprintk(ctx->dev, 1,
+ "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n",
+ type_name(f->type), q_data->width, q_data->height,
+ q_data->fmt->depth,
+ (q_data->fmt->fourcc & 0xff),
+ (q_data->fmt->fourcc >> 8) & 0xff,
+ (q_data->fmt->fourcc >> 16) & 0xff,
+ (q_data->fmt->fourcc >> 24) & 0xff);
return 0;
}
@@ -674,6 +930,8 @@ static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TRANS_TIME_MSEC:
ctx->transtime = ctrl->val;
+ if (ctx->transtime < 1)
+ ctx->transtime = 1;
break;
case V4L2_CID_TRANS_NUM_BUFS:
@@ -692,11 +950,11 @@ static const struct v4l2_ctrl_ops vim2m_ctrl_ops = {
.s_ctrl = vim2m_s_ctrl,
};
-
static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
.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,
@@ -721,20 +979,23 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-
/*
* Queue operations
*/
static int vim2m_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
struct vim2m_q_data *q_data;
unsigned int size, count = *nbuffers;
q_data = get_q_data(ctx, vq->type);
+ if (!q_data)
+ return -EINVAL;
size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
@@ -748,33 +1009,42 @@ static int vim2m_queue_setup(struct vb2_queue *vq,
*nplanes = 1;
sizes[0] = size;
- dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+ dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n",
+ type_name(vq->type), count, size);
return 0;
}
-static int vim2m_buf_prepare(struct vb2_buffer *vb)
+static int vim2m_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE) {
+ dprintk(ctx->dev, 1, "%s field isn't supported\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vim2m_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct vim2m_q_data *q_data;
- dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+ dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type));
q_data = get_q_data(ctx, vb->vb2_queue->type);
- if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
- if (vbuf->field == V4L2_FIELD_ANY)
- vbuf->field = V4L2_FIELD_NONE;
- if (vbuf->field != V4L2_FIELD_NONE) {
- dprintk(ctx->dev, "%s field isn't supported\n",
- __func__);
- return -EINVAL;
- }
- }
-
+ if (!q_data)
+ return -EINVAL;
if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
- dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
+ dprintk(ctx->dev, 1,
+ "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
return -EINVAL;
}
@@ -791,11 +1061,14 @@ static void vim2m_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
-static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
+static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
struct vim2m_q_data *q_data = get_q_data(ctx, q->type);
+ if (!q_data)
+ return -EINVAL;
+
q_data->sequence = 0;
return 0;
}
@@ -803,25 +1076,23 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
static void vim2m_stop_streaming(struct vb2_queue *q)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
- struct vim2m_dev *dev = ctx->dev;
struct vb2_v4l2_buffer *vbuf;
unsigned long flags;
- if (v4l2_m2m_get_curr_priv(dev->m2m_dev) == ctx)
- cancel_delayed_work_sync(&dev->work_run);
+ cancel_delayed_work_sync(&ctx->work_run);
for (;;) {
if (V4L2_TYPE_IS_OUTPUT(q->type))
vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
else
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (vbuf == NULL)
+ if (!vbuf)
return;
v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
&ctx->hdl);
- spin_lock_irqsave(&ctx->dev->irqlock, flags);
+ spin_lock_irqsave(&ctx->irqlock, flags);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
- spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+ spin_unlock_irqrestore(&ctx->irqlock, flags);
}
}
@@ -834,6 +1105,7 @@ static void vim2m_buf_request_complete(struct vb2_buffer *vb)
static const struct vb2_ops vim2m_qops = {
.queue_setup = vim2m_queue_setup,
+ .buf_out_validate = vim2m_buf_out_validate,
.buf_prepare = vim2m_buf_prepare,
.buf_queue = vim2m_buf_queue,
.start_streaming = vim2m_start_streaming,
@@ -843,7 +1115,8 @@ static const struct vb2_ops vim2m_qops = {
.buf_request_complete = vim2m_buf_request_complete,
};
-static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
{
struct vim2m_ctx *ctx = priv;
int ret;
@@ -855,7 +1128,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
src_vq->ops = &vim2m_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- src_vq->lock = &ctx->dev->dev_mutex;
+ src_vq->lock = &ctx->vb_mutex;
src_vq->supports_requests = true;
ret = vb2_queue_init(src_vq);
@@ -869,17 +1142,16 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
dst_vq->ops = &vim2m_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- dst_vq->lock = &ctx->dev->dev_mutex;
+ dst_vq->lock = &ctx->vb_mutex;
return vb2_queue_init(dst_vq);
}
-static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
+static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
.ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
- .def = MEM2MEM_DEF_TRANSTIME,
.min = 1,
.max = 10001,
.step = 1,
@@ -921,6 +1193,8 @@ static int vim2m_open(struct file *file)
v4l2_ctrl_handler_init(hdl, 4);
v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ vim2m_ctrl_trans_time_msec.def = default_transtime;
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
if (hdl->error) {
@@ -944,6 +1218,10 @@ static int vim2m_open(struct file *file)
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ mutex_init(&ctx->vb_mutex);
+ spin_lock_init(&ctx->irqlock);
+ INIT_DELAYED_WORK(&ctx->work_run, device_work);
+
if (IS_ERR(ctx->fh.m2m_ctx)) {
rc = PTR_ERR(ctx->fh.m2m_ctx);
@@ -956,7 +1234,7 @@ static int vim2m_open(struct file *file)
v4l2_fh_add(&ctx->fh);
atomic_inc(&dev->num_inst);
- dprintk(dev, "Created instance: %p, m2m_ctx: %p\n",
+ dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n",
ctx, ctx->fh.m2m_ctx);
open_unlock:
@@ -969,7 +1247,7 @@ static int vim2m_release(struct file *file)
struct vim2m_dev *dev = video_drvdata(file);
struct vim2m_ctx *ctx = file2ctx(file);
- dprintk(dev, "Releasing instance %p\n", ctx);
+ dprintk(dev, 1, "Releasing instance %p\n", ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
@@ -1024,8 +1302,6 @@ static int vim2m_probe(struct platform_device *pdev)
if (!dev)
return -ENOMEM;
- spin_lock_init(&dev->irqlock);
-
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
return ret;
@@ -1037,7 +1313,6 @@ static int vim2m_probe(struct platform_device *pdev)
vfd = &dev->vfd;
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
- INIT_DELAYED_WORK(&dev->work_run, device_work);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -1047,7 +1322,7 @@ static int vim2m_probe(struct platform_device *pdev)
video_set_drvdata(vfd, dev);
v4l2_info(&dev->v4l2_dev,
- "Device registered as /dev/video%d\n", vfd->num);
+ "Device registered as /dev/video%d\n", vfd->num);
platform_set_drvdata(pdev, dev);
@@ -1061,12 +1336,14 @@ static int vim2m_probe(struct platform_device *pdev)
#ifdef CONFIG_MEDIA_CONTROLLER
dev->mdev.dev = &pdev->dev;
strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
+ strscpy(dev->mdev.bus_info, "platform:vim2m",
+ sizeof(dev->mdev.bus_info));
media_device_init(&dev->mdev);
dev->mdev.ops = &m2m_media_ops;
dev->v4l2_dev.mdev = &dev->mdev;
- ret = v4l2_m2m_register_media_controller(dev->m2m_dev,
- vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER);
+ ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
+ MEDIA_ENT_F_PROC_VIDEO_SCALER);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");
goto unreg_m2m;