1dacca5f0SHans Verkuil // SPDX-License-Identifier: GPL-2.0+ 2dacca5f0SHans Verkuil /* 3dacca5f0SHans Verkuil * A virtual v4l2-mem2mem example device. 4dacca5f0SHans Verkuil * 5dacca5f0SHans Verkuil * This is a virtual device driver for testing mem-to-mem videobuf framework. 6dacca5f0SHans Verkuil * It simulates a device that uses memory buffers for both source and 7dacca5f0SHans Verkuil * destination, processes the data and issues an "irq" (simulated by a delayed 8dacca5f0SHans Verkuil * workqueue). 9dacca5f0SHans Verkuil * The device is capable of multi-instance, multi-buffer-per-transaction 10dacca5f0SHans Verkuil * operation (via the mem2mem framework). 11dacca5f0SHans Verkuil * 12dacca5f0SHans Verkuil * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. 13dacca5f0SHans Verkuil * Pawel Osciak, <pawel@osciak.com> 14dacca5f0SHans Verkuil * Marek Szyprowski, <m.szyprowski@samsung.com> 15dacca5f0SHans Verkuil * 16dacca5f0SHans Verkuil * This program is free software; you can redistribute it and/or modify 17dacca5f0SHans Verkuil * it under the terms of the GNU General Public License as published by the 18dacca5f0SHans Verkuil * Free Software Foundation; either version 2 of the 19dacca5f0SHans Verkuil * License, or (at your option) any later version 20dacca5f0SHans Verkuil */ 21dacca5f0SHans Verkuil #include <linux/module.h> 22dacca5f0SHans Verkuil #include <linux/delay.h> 23dacca5f0SHans Verkuil #include <linux/fs.h> 24dacca5f0SHans Verkuil #include <linux/sched.h> 25dacca5f0SHans Verkuil #include <linux/slab.h> 26dacca5f0SHans Verkuil 27dacca5f0SHans Verkuil #include <linux/platform_device.h> 28dacca5f0SHans Verkuil #include <media/v4l2-mem2mem.h> 29dacca5f0SHans Verkuil #include <media/v4l2-device.h> 30dacca5f0SHans Verkuil #include <media/v4l2-ioctl.h> 31dacca5f0SHans Verkuil #include <media/v4l2-ctrls.h> 32dacca5f0SHans Verkuil #include <media/v4l2-event.h> 33dacca5f0SHans Verkuil #include <media/videobuf2-vmalloc.h> 34dacca5f0SHans Verkuil 35dacca5f0SHans Verkuil MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); 36dacca5f0SHans Verkuil MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>"); 37dacca5f0SHans Verkuil MODULE_LICENSE("GPL"); 38dacca5f0SHans Verkuil MODULE_VERSION("0.2"); 39dacca5f0SHans Verkuil MODULE_ALIAS("mem2mem_testdev"); 40dacca5f0SHans Verkuil 41dacca5f0SHans Verkuil static unsigned int debug; 42dacca5f0SHans Verkuil module_param(debug, uint, 0644); 43dacca5f0SHans Verkuil MODULE_PARM_DESC(debug, "debug level"); 44dacca5f0SHans Verkuil 45dacca5f0SHans Verkuil /* Default transaction time in msec */ 46dacca5f0SHans Verkuil static unsigned int default_transtime = 40; /* Max 25 fps */ 47dacca5f0SHans Verkuil module_param(default_transtime, uint, 0644); 48dacca5f0SHans Verkuil MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); 49dacca5f0SHans Verkuil 50dacca5f0SHans Verkuil #define MIN_W 32 51dacca5f0SHans Verkuil #define MIN_H 32 52dacca5f0SHans Verkuil #define MAX_W 640 53dacca5f0SHans Verkuil #define MAX_H 480 54dacca5f0SHans Verkuil 55dacca5f0SHans Verkuil /* Pixel alignment for non-bayer formats */ 56dacca5f0SHans Verkuil #define WIDTH_ALIGN 2 57dacca5f0SHans Verkuil #define HEIGHT_ALIGN 1 58dacca5f0SHans Verkuil 59dacca5f0SHans Verkuil /* Pixel alignment for bayer formats */ 60dacca5f0SHans Verkuil #define BAYER_WIDTH_ALIGN 2 61dacca5f0SHans Verkuil #define BAYER_HEIGHT_ALIGN 2 62dacca5f0SHans Verkuil 63dacca5f0SHans Verkuil /* Flags that indicate a format can be used for capture/output */ 64dacca5f0SHans Verkuil #define MEM2MEM_CAPTURE BIT(0) 65dacca5f0SHans Verkuil #define MEM2MEM_OUTPUT BIT(1) 66dacca5f0SHans Verkuil 67dacca5f0SHans Verkuil #define MEM2MEM_NAME "vim2m" 68dacca5f0SHans Verkuil 69dacca5f0SHans Verkuil /* Per queue */ 70dacca5f0SHans Verkuil #define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME 71dacca5f0SHans Verkuil /* In bytes, per queue */ 72dacca5f0SHans Verkuil #define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) 73dacca5f0SHans Verkuil 74dacca5f0SHans Verkuil /* Flags that indicate processing mode */ 75dacca5f0SHans Verkuil #define MEM2MEM_HFLIP BIT(0) 76dacca5f0SHans Verkuil #define MEM2MEM_VFLIP BIT(1) 77dacca5f0SHans Verkuil 78dacca5f0SHans Verkuil #define dprintk(dev, lvl, fmt, arg...) \ 79dacca5f0SHans Verkuil v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) 80dacca5f0SHans Verkuil 81dacca5f0SHans Verkuil static void vim2m_dev_release(struct device *dev) 82dacca5f0SHans Verkuil {} 83dacca5f0SHans Verkuil 84dacca5f0SHans Verkuil static struct platform_device vim2m_pdev = { 85dacca5f0SHans Verkuil .name = MEM2MEM_NAME, 86dacca5f0SHans Verkuil .dev.release = vim2m_dev_release, 87dacca5f0SHans Verkuil }; 88dacca5f0SHans Verkuil 89dacca5f0SHans Verkuil struct vim2m_fmt { 90dacca5f0SHans Verkuil u32 fourcc; 91dacca5f0SHans Verkuil int depth; 92dacca5f0SHans Verkuil /* Types the format can be used for */ 93dacca5f0SHans Verkuil u32 types; 94dacca5f0SHans Verkuil }; 95dacca5f0SHans Verkuil 96dacca5f0SHans Verkuil static struct vim2m_fmt formats[] = { 97dacca5f0SHans Verkuil { 98dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */ 99dacca5f0SHans Verkuil .depth = 16, 100dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, 101dacca5f0SHans Verkuil }, { 102dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */ 103dacca5f0SHans Verkuil .depth = 16, 104dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, 105dacca5f0SHans Verkuil }, { 106dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_RGB24, 107dacca5f0SHans Verkuil .depth = 24, 108dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, 109dacca5f0SHans Verkuil }, { 110dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_BGR24, 111dacca5f0SHans Verkuil .depth = 24, 112dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, 113dacca5f0SHans Verkuil }, { 114dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_YUYV, 115dacca5f0SHans Verkuil .depth = 16, 116dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE, 117dacca5f0SHans Verkuil }, { 118dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_SBGGR8, 119dacca5f0SHans Verkuil .depth = 8, 120dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE, 121dacca5f0SHans Verkuil }, { 122dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_SGBRG8, 123dacca5f0SHans Verkuil .depth = 8, 124dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE, 125dacca5f0SHans Verkuil }, { 126dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_SGRBG8, 127dacca5f0SHans Verkuil .depth = 8, 128dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE, 129dacca5f0SHans Verkuil }, { 130dacca5f0SHans Verkuil .fourcc = V4L2_PIX_FMT_SRGGB8, 131dacca5f0SHans Verkuil .depth = 8, 132dacca5f0SHans Verkuil .types = MEM2MEM_CAPTURE, 133dacca5f0SHans Verkuil }, 134dacca5f0SHans Verkuil }; 135dacca5f0SHans Verkuil 136dacca5f0SHans Verkuil #define NUM_FORMATS ARRAY_SIZE(formats) 137dacca5f0SHans Verkuil 138dacca5f0SHans Verkuil /* Per-queue, driver-specific private data */ 139dacca5f0SHans Verkuil struct vim2m_q_data { 140dacca5f0SHans Verkuil unsigned int width; 141dacca5f0SHans Verkuil unsigned int height; 142dacca5f0SHans Verkuil unsigned int sizeimage; 143dacca5f0SHans Verkuil unsigned int sequence; 144dacca5f0SHans Verkuil struct vim2m_fmt *fmt; 145dacca5f0SHans Verkuil }; 146dacca5f0SHans Verkuil 147dacca5f0SHans Verkuil enum { 148dacca5f0SHans Verkuil V4L2_M2M_SRC = 0, 149dacca5f0SHans Verkuil V4L2_M2M_DST = 1, 150dacca5f0SHans Verkuil }; 151dacca5f0SHans Verkuil 152dacca5f0SHans Verkuil #define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000) 153dacca5f0SHans Verkuil #define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001) 154dacca5f0SHans Verkuil 155dacca5f0SHans Verkuil static struct vim2m_fmt *find_format(u32 fourcc) 156dacca5f0SHans Verkuil { 157dacca5f0SHans Verkuil struct vim2m_fmt *fmt; 158dacca5f0SHans Verkuil unsigned int k; 159dacca5f0SHans Verkuil 160dacca5f0SHans Verkuil for (k = 0; k < NUM_FORMATS; k++) { 161dacca5f0SHans Verkuil fmt = &formats[k]; 162dacca5f0SHans Verkuil if (fmt->fourcc == fourcc) 163dacca5f0SHans Verkuil break; 164dacca5f0SHans Verkuil } 165dacca5f0SHans Verkuil 166dacca5f0SHans Verkuil if (k == NUM_FORMATS) 167dacca5f0SHans Verkuil return NULL; 168dacca5f0SHans Verkuil 169dacca5f0SHans Verkuil return &formats[k]; 170dacca5f0SHans Verkuil } 171dacca5f0SHans Verkuil 172dacca5f0SHans Verkuil static void get_alignment(u32 fourcc, 173dacca5f0SHans Verkuil unsigned int *walign, unsigned int *halign) 174dacca5f0SHans Verkuil { 175dacca5f0SHans Verkuil switch (fourcc) { 176dacca5f0SHans Verkuil case V4L2_PIX_FMT_SBGGR8: 177dacca5f0SHans Verkuil case V4L2_PIX_FMT_SGBRG8: 178dacca5f0SHans Verkuil case V4L2_PIX_FMT_SGRBG8: 179dacca5f0SHans Verkuil case V4L2_PIX_FMT_SRGGB8: 180dacca5f0SHans Verkuil *walign = BAYER_WIDTH_ALIGN; 181dacca5f0SHans Verkuil *halign = BAYER_HEIGHT_ALIGN; 182dacca5f0SHans Verkuil return; 183dacca5f0SHans Verkuil default: 184dacca5f0SHans Verkuil *walign = WIDTH_ALIGN; 185dacca5f0SHans Verkuil *halign = HEIGHT_ALIGN; 186dacca5f0SHans Verkuil return; 187dacca5f0SHans Verkuil } 188dacca5f0SHans Verkuil } 189dacca5f0SHans Verkuil 190dacca5f0SHans Verkuil struct vim2m_dev { 191dacca5f0SHans Verkuil struct v4l2_device v4l2_dev; 192dacca5f0SHans Verkuil struct video_device vfd; 193dacca5f0SHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 194dacca5f0SHans Verkuil struct media_device mdev; 195dacca5f0SHans Verkuil #endif 196dacca5f0SHans Verkuil 197dacca5f0SHans Verkuil atomic_t num_inst; 198dacca5f0SHans Verkuil struct mutex dev_mutex; 199dacca5f0SHans Verkuil 200dacca5f0SHans Verkuil struct v4l2_m2m_dev *m2m_dev; 201dacca5f0SHans Verkuil }; 202dacca5f0SHans Verkuil 203dacca5f0SHans Verkuil struct vim2m_ctx { 204dacca5f0SHans Verkuil struct v4l2_fh fh; 205dacca5f0SHans Verkuil struct vim2m_dev *dev; 206dacca5f0SHans Verkuil 207dacca5f0SHans Verkuil struct v4l2_ctrl_handler hdl; 208dacca5f0SHans Verkuil 209dacca5f0SHans Verkuil /* Processed buffers in this transaction */ 210dacca5f0SHans Verkuil u8 num_processed; 211dacca5f0SHans Verkuil 212dacca5f0SHans Verkuil /* Transaction length (i.e. how many buffers per transaction) */ 213dacca5f0SHans Verkuil u32 translen; 214dacca5f0SHans Verkuil /* Transaction time (i.e. simulated processing time) in milliseconds */ 215dacca5f0SHans Verkuil u32 transtime; 216dacca5f0SHans Verkuil 217dacca5f0SHans Verkuil struct mutex vb_mutex; 218dacca5f0SHans Verkuil struct delayed_work work_run; 219dacca5f0SHans Verkuil 220dacca5f0SHans Verkuil /* Abort requested by m2m */ 221dacca5f0SHans Verkuil int aborting; 222dacca5f0SHans Verkuil 223dacca5f0SHans Verkuil /* Processing mode */ 224dacca5f0SHans Verkuil int mode; 225dacca5f0SHans Verkuil 226dacca5f0SHans Verkuil enum v4l2_colorspace colorspace; 227dacca5f0SHans Verkuil enum v4l2_ycbcr_encoding ycbcr_enc; 228dacca5f0SHans Verkuil enum v4l2_xfer_func xfer_func; 229dacca5f0SHans Verkuil enum v4l2_quantization quant; 230dacca5f0SHans Verkuil 231dacca5f0SHans Verkuil /* Source and destination queue data */ 232dacca5f0SHans Verkuil struct vim2m_q_data q_data[2]; 233dacca5f0SHans Verkuil }; 234dacca5f0SHans Verkuil 235dacca5f0SHans Verkuil static inline struct vim2m_ctx *file2ctx(struct file *file) 236dacca5f0SHans Verkuil { 237dacca5f0SHans Verkuil return container_of(file->private_data, struct vim2m_ctx, fh); 238dacca5f0SHans Verkuil } 239dacca5f0SHans Verkuil 240dacca5f0SHans Verkuil static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, 241dacca5f0SHans Verkuil enum v4l2_buf_type type) 242dacca5f0SHans Verkuil { 243dacca5f0SHans Verkuil switch (type) { 244dacca5f0SHans Verkuil case V4L2_BUF_TYPE_VIDEO_OUTPUT: 245dacca5f0SHans Verkuil return &ctx->q_data[V4L2_M2M_SRC]; 246dacca5f0SHans Verkuil case V4L2_BUF_TYPE_VIDEO_CAPTURE: 247dacca5f0SHans Verkuil return &ctx->q_data[V4L2_M2M_DST]; 248dacca5f0SHans Verkuil default: 249dacca5f0SHans Verkuil return NULL; 250dacca5f0SHans Verkuil } 251dacca5f0SHans Verkuil } 252dacca5f0SHans Verkuil 253dacca5f0SHans Verkuil static const char *type_name(enum v4l2_buf_type type) 254dacca5f0SHans Verkuil { 255dacca5f0SHans Verkuil switch (type) { 256dacca5f0SHans Verkuil case V4L2_BUF_TYPE_VIDEO_OUTPUT: 257dacca5f0SHans Verkuil return "Output"; 258dacca5f0SHans Verkuil case V4L2_BUF_TYPE_VIDEO_CAPTURE: 259dacca5f0SHans Verkuil return "Capture"; 260dacca5f0SHans Verkuil default: 261dacca5f0SHans Verkuil return "Invalid"; 262dacca5f0SHans Verkuil } 263dacca5f0SHans Verkuil } 264dacca5f0SHans Verkuil 265dacca5f0SHans Verkuil #define CLIP(__color) \ 266dacca5f0SHans Verkuil (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color))) 267dacca5f0SHans Verkuil 268dacca5f0SHans Verkuil static void copy_line(struct vim2m_q_data *q_data_out, 269dacca5f0SHans Verkuil u8 *src, u8 *dst, bool reverse) 270dacca5f0SHans Verkuil { 271dacca5f0SHans Verkuil int x, depth = q_data_out->fmt->depth >> 3; 272dacca5f0SHans Verkuil 273dacca5f0SHans Verkuil if (!reverse) { 274dacca5f0SHans Verkuil memcpy(dst, src, q_data_out->width * depth); 275dacca5f0SHans Verkuil } else { 276dacca5f0SHans Verkuil for (x = 0; x < q_data_out->width >> 1; x++) { 277dacca5f0SHans Verkuil memcpy(dst, src, depth); 278dacca5f0SHans Verkuil memcpy(dst + depth, src - depth, depth); 279dacca5f0SHans Verkuil src -= depth << 1; 280dacca5f0SHans Verkuil dst += depth << 1; 281dacca5f0SHans Verkuil } 282dacca5f0SHans Verkuil return; 283dacca5f0SHans Verkuil } 284dacca5f0SHans Verkuil } 285dacca5f0SHans Verkuil 286dacca5f0SHans Verkuil static void copy_two_pixels(struct vim2m_q_data *q_data_in, 287dacca5f0SHans Verkuil struct vim2m_q_data *q_data_out, 288dacca5f0SHans Verkuil u8 *src[2], u8 **dst, int ypos, bool reverse) 289dacca5f0SHans Verkuil { 290dacca5f0SHans Verkuil struct vim2m_fmt *out = q_data_out->fmt; 291dacca5f0SHans Verkuil struct vim2m_fmt *in = q_data_in->fmt; 292dacca5f0SHans Verkuil u8 _r[2], _g[2], _b[2], *r, *g, *b; 293dacca5f0SHans Verkuil int i; 294dacca5f0SHans Verkuil 295dacca5f0SHans Verkuil /* Step 1: read two consecutive pixels from src pointer */ 296dacca5f0SHans Verkuil 297dacca5f0SHans Verkuil r = _r; 298dacca5f0SHans Verkuil g = _g; 299dacca5f0SHans Verkuil b = _b; 300dacca5f0SHans Verkuil 301dacca5f0SHans Verkuil switch (in->fourcc) { 302dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ 303dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 304dacca5f0SHans Verkuil u16 pix = le16_to_cpu(*(__le16 *)(src[i])); 305dacca5f0SHans Verkuil 306dacca5f0SHans Verkuil *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07; 307dacca5f0SHans Verkuil *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03; 308dacca5f0SHans Verkuil *b++ = (u8)((pix & 0x1f) << 3) | 0x07; 309dacca5f0SHans Verkuil } 310dacca5f0SHans Verkuil break; 311dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ 312dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 313dacca5f0SHans Verkuil u16 pix = be16_to_cpu(*(__be16 *)(src[i])); 314dacca5f0SHans Verkuil 315dacca5f0SHans Verkuil *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07; 316dacca5f0SHans Verkuil *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03; 317dacca5f0SHans Verkuil *b++ = (u8)((pix & 0x1f) << 3) | 0x07; 318dacca5f0SHans Verkuil } 319dacca5f0SHans Verkuil break; 320dacca5f0SHans Verkuil default: 321dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB24: 322dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 323dacca5f0SHans Verkuil *r++ = src[i][0]; 324dacca5f0SHans Verkuil *g++ = src[i][1]; 325dacca5f0SHans Verkuil *b++ = src[i][2]; 326dacca5f0SHans Verkuil } 327dacca5f0SHans Verkuil break; 328dacca5f0SHans Verkuil case V4L2_PIX_FMT_BGR24: 329dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 330dacca5f0SHans Verkuil *b++ = src[i][0]; 331dacca5f0SHans Verkuil *g++ = src[i][1]; 332dacca5f0SHans Verkuil *r++ = src[i][2]; 333dacca5f0SHans Verkuil } 334dacca5f0SHans Verkuil break; 335dacca5f0SHans Verkuil } 336dacca5f0SHans Verkuil 337dacca5f0SHans Verkuil /* Step 2: store two consecutive points, reversing them if needed */ 338dacca5f0SHans Verkuil 339dacca5f0SHans Verkuil r = _r; 340dacca5f0SHans Verkuil g = _g; 341dacca5f0SHans Verkuil b = _b; 342dacca5f0SHans Verkuil 343dacca5f0SHans Verkuil switch (out->fourcc) { 344dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ 345dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 346dacca5f0SHans Verkuil u16 pix; 347dacca5f0SHans Verkuil __le16 *dst_pix = (__le16 *)*dst; 348dacca5f0SHans Verkuil 349dacca5f0SHans Verkuil pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) | 350dacca5f0SHans Verkuil (*b >> 3); 351dacca5f0SHans Verkuil 352dacca5f0SHans Verkuil *dst_pix = cpu_to_le16(pix); 353dacca5f0SHans Verkuil 354dacca5f0SHans Verkuil *dst += 2; 355dacca5f0SHans Verkuil } 356dacca5f0SHans Verkuil return; 357dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ 358dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 359dacca5f0SHans Verkuil u16 pix; 360dacca5f0SHans Verkuil __be16 *dst_pix = (__be16 *)*dst; 361dacca5f0SHans Verkuil 362dacca5f0SHans Verkuil pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) | 363dacca5f0SHans Verkuil (*b >> 3); 364dacca5f0SHans Verkuil 365dacca5f0SHans Verkuil *dst_pix = cpu_to_be16(pix); 366dacca5f0SHans Verkuil 367dacca5f0SHans Verkuil *dst += 2; 368dacca5f0SHans Verkuil } 369dacca5f0SHans Verkuil return; 370dacca5f0SHans Verkuil case V4L2_PIX_FMT_RGB24: 371dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 372dacca5f0SHans Verkuil *(*dst)++ = *r++; 373dacca5f0SHans Verkuil *(*dst)++ = *g++; 374dacca5f0SHans Verkuil *(*dst)++ = *b++; 375dacca5f0SHans Verkuil } 376dacca5f0SHans Verkuil return; 377dacca5f0SHans Verkuil case V4L2_PIX_FMT_BGR24: 378dacca5f0SHans Verkuil for (i = 0; i < 2; i++) { 379dacca5f0SHans Verkuil *(*dst)++ = *b++; 380dacca5f0SHans Verkuil *(*dst)++ = *g++; 381dacca5f0SHans Verkuil *(*dst)++ = *r++; 382dacca5f0SHans Verkuil } 383dacca5f0SHans Verkuil return; 384dacca5f0SHans Verkuil case V4L2_PIX_FMT_YUYV: 385dacca5f0SHans Verkuil default: 386dacca5f0SHans Verkuil { 387dacca5f0SHans Verkuil u8 y, y1, u, v; 388dacca5f0SHans Verkuil 389dacca5f0SHans Verkuil y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) 390dacca5f0SHans Verkuil + 524288) >> 15); 391dacca5f0SHans Verkuil u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b) 392dacca5f0SHans Verkuil + 4210688) >> 15); 393dacca5f0SHans Verkuil v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++) 394dacca5f0SHans Verkuil + 4210688) >> 15); 395dacca5f0SHans Verkuil y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) 396dacca5f0SHans Verkuil + 524288) >> 15); 397dacca5f0SHans Verkuil 398dacca5f0SHans Verkuil *(*dst)++ = y; 399dacca5f0SHans Verkuil *(*dst)++ = u; 400dacca5f0SHans Verkuil 401dacca5f0SHans Verkuil *(*dst)++ = y1; 402dacca5f0SHans Verkuil *(*dst)++ = v; 403dacca5f0SHans Verkuil return; 404dacca5f0SHans Verkuil } 405dacca5f0SHans Verkuil case V4L2_PIX_FMT_SBGGR8: 406dacca5f0SHans Verkuil if (!(ypos & 1)) { 407dacca5f0SHans Verkuil *(*dst)++ = *b; 408dacca5f0SHans Verkuil *(*dst)++ = *++g; 409dacca5f0SHans Verkuil } else { 410dacca5f0SHans Verkuil *(*dst)++ = *g; 411dacca5f0SHans Verkuil *(*dst)++ = *++r; 412dacca5f0SHans Verkuil } 413dacca5f0SHans Verkuil return; 414dacca5f0SHans Verkuil case V4L2_PIX_FMT_SGBRG8: 415dacca5f0SHans Verkuil if (!(ypos & 1)) { 416dacca5f0SHans Verkuil *(*dst)++ = *g; 417dacca5f0SHans Verkuil *(*dst)++ = *++b; 418dacca5f0SHans Verkuil } else { 419dacca5f0SHans Verkuil *(*dst)++ = *r; 420dacca5f0SHans Verkuil *(*dst)++ = *++g; 421dacca5f0SHans Verkuil } 422dacca5f0SHans Verkuil return; 423dacca5f0SHans Verkuil case V4L2_PIX_FMT_SGRBG8: 424dacca5f0SHans Verkuil if (!(ypos & 1)) { 425dacca5f0SHans Verkuil *(*dst)++ = *g; 426dacca5f0SHans Verkuil *(*dst)++ = *++r; 427dacca5f0SHans Verkuil } else { 428dacca5f0SHans Verkuil *(*dst)++ = *b; 429dacca5f0SHans Verkuil *(*dst)++ = *++g; 430dacca5f0SHans Verkuil } 431dacca5f0SHans Verkuil return; 432dacca5f0SHans Verkuil case V4L2_PIX_FMT_SRGGB8: 433dacca5f0SHans Verkuil if (!(ypos & 1)) { 434dacca5f0SHans Verkuil *(*dst)++ = *r; 435dacca5f0SHans Verkuil *(*dst)++ = *++g; 436dacca5f0SHans Verkuil } else { 437dacca5f0SHans Verkuil *(*dst)++ = *g; 438dacca5f0SHans Verkuil *(*dst)++ = *++b; 439dacca5f0SHans Verkuil } 440dacca5f0SHans Verkuil return; 441dacca5f0SHans Verkuil } 442dacca5f0SHans Verkuil } 443dacca5f0SHans Verkuil 444dacca5f0SHans Verkuil static int device_process(struct vim2m_ctx *ctx, 445dacca5f0SHans Verkuil struct vb2_v4l2_buffer *in_vb, 446dacca5f0SHans Verkuil struct vb2_v4l2_buffer *out_vb) 447dacca5f0SHans Verkuil { 448dacca5f0SHans Verkuil struct vim2m_dev *dev = ctx->dev; 449dacca5f0SHans Verkuil struct vim2m_q_data *q_data_in, *q_data_out; 450dacca5f0SHans Verkuil u8 *p_in, *p_line, *p_in_x[2], *p, *p_out; 451dacca5f0SHans Verkuil unsigned int width, height, bytesperline, bytes_per_pixel; 452dacca5f0SHans Verkuil unsigned int x, y, y_in, y_out, x_int, x_fract, x_err, x_offset; 453dacca5f0SHans Verkuil int start, end, step; 454dacca5f0SHans Verkuil 455dacca5f0SHans Verkuil q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 456dacca5f0SHans Verkuil if (!q_data_in) 457dacca5f0SHans Verkuil return 0; 458dacca5f0SHans Verkuil bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3; 459dacca5f0SHans Verkuil bytes_per_pixel = q_data_in->fmt->depth >> 3; 460dacca5f0SHans Verkuil 461dacca5f0SHans Verkuil q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 462dacca5f0SHans Verkuil if (!q_data_out) 463dacca5f0SHans Verkuil return 0; 464dacca5f0SHans Verkuil 465dacca5f0SHans Verkuil /* As we're doing scaling, use the output dimensions here */ 466dacca5f0SHans Verkuil height = q_data_out->height; 467dacca5f0SHans Verkuil width = q_data_out->width; 468dacca5f0SHans Verkuil 469dacca5f0SHans Verkuil p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); 470dacca5f0SHans Verkuil p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); 471dacca5f0SHans Verkuil if (!p_in || !p_out) { 472dacca5f0SHans Verkuil v4l2_err(&dev->v4l2_dev, 473dacca5f0SHans Verkuil "Acquiring kernel pointers to buffers failed\n"); 474dacca5f0SHans Verkuil return -EFAULT; 475dacca5f0SHans Verkuil } 476dacca5f0SHans Verkuil 477dacca5f0SHans Verkuil out_vb->sequence = q_data_out->sequence++; 478dacca5f0SHans Verkuil in_vb->sequence = q_data_in->sequence++; 479dacca5f0SHans Verkuil v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); 480dacca5f0SHans Verkuil 481dacca5f0SHans Verkuil if (ctx->mode & MEM2MEM_VFLIP) { 482dacca5f0SHans Verkuil start = height - 1; 483dacca5f0SHans Verkuil end = -1; 484dacca5f0SHans Verkuil step = -1; 485dacca5f0SHans Verkuil } else { 486dacca5f0SHans Verkuil start = 0; 487dacca5f0SHans Verkuil end = height; 488dacca5f0SHans Verkuil step = 1; 489dacca5f0SHans Verkuil } 490dacca5f0SHans Verkuil y_out = 0; 491dacca5f0SHans Verkuil 492dacca5f0SHans Verkuil /* 493dacca5f0SHans Verkuil * When format and resolution are identical, 494dacca5f0SHans Verkuil * we can use a faster copy logic 495dacca5f0SHans Verkuil */ 496dacca5f0SHans Verkuil if (q_data_in->fmt->fourcc == q_data_out->fmt->fourcc && 497dacca5f0SHans Verkuil q_data_in->width == q_data_out->width && 498dacca5f0SHans Verkuil q_data_in->height == q_data_out->height) { 499dacca5f0SHans Verkuil for (y = start; y != end; y += step, y_out++) { 500dacca5f0SHans Verkuil p = p_in + (y * bytesperline); 501dacca5f0SHans Verkuil if (ctx->mode & MEM2MEM_HFLIP) 502dacca5f0SHans Verkuil p += bytesperline - (q_data_in->fmt->depth >> 3); 503dacca5f0SHans Verkuil 504dacca5f0SHans Verkuil copy_line(q_data_out, p, p_out, 505dacca5f0SHans Verkuil ctx->mode & MEM2MEM_HFLIP); 506dacca5f0SHans Verkuil 507dacca5f0SHans Verkuil p_out += bytesperline; 508dacca5f0SHans Verkuil } 509dacca5f0SHans Verkuil return 0; 510dacca5f0SHans Verkuil } 511dacca5f0SHans Verkuil 512dacca5f0SHans Verkuil /* Slower algorithm with format conversion, hflip, vflip and scaler */ 513dacca5f0SHans Verkuil 514dacca5f0SHans Verkuil /* To speed scaler up, use Bresenham for X dimension */ 515dacca5f0SHans Verkuil x_int = q_data_in->width / q_data_out->width; 516dacca5f0SHans Verkuil x_fract = q_data_in->width % q_data_out->width; 517dacca5f0SHans Verkuil 518dacca5f0SHans Verkuil for (y = start; y != end; y += step, y_out++) { 519dacca5f0SHans Verkuil y_in = (y * q_data_in->height) / q_data_out->height; 520dacca5f0SHans Verkuil x_offset = 0; 521dacca5f0SHans Verkuil x_err = 0; 522dacca5f0SHans Verkuil 523dacca5f0SHans Verkuil p_line = p_in + (y_in * bytesperline); 524dacca5f0SHans Verkuil if (ctx->mode & MEM2MEM_HFLIP) 525dacca5f0SHans Verkuil p_line += bytesperline - (q_data_in->fmt->depth >> 3); 526dacca5f0SHans Verkuil p_in_x[0] = p_line; 527dacca5f0SHans Verkuil 528dacca5f0SHans Verkuil for (x = 0; x < width >> 1; x++) { 529dacca5f0SHans Verkuil x_offset += x_int; 530dacca5f0SHans Verkuil x_err += x_fract; 531dacca5f0SHans Verkuil if (x_err > width) { 532dacca5f0SHans Verkuil x_offset++; 533dacca5f0SHans Verkuil x_err -= width; 534dacca5f0SHans Verkuil } 535dacca5f0SHans Verkuil 536dacca5f0SHans Verkuil if (ctx->mode & MEM2MEM_HFLIP) 537dacca5f0SHans Verkuil p_in_x[1] = p_line - x_offset * bytes_per_pixel; 538dacca5f0SHans Verkuil else 539dacca5f0SHans Verkuil p_in_x[1] = p_line + x_offset * bytes_per_pixel; 540dacca5f0SHans Verkuil 541dacca5f0SHans Verkuil copy_two_pixels(q_data_in, q_data_out, 542dacca5f0SHans Verkuil p_in_x, &p_out, y_out, 543dacca5f0SHans Verkuil ctx->mode & MEM2MEM_HFLIP); 544dacca5f0SHans Verkuil 545dacca5f0SHans Verkuil /* Calculate the next p_in_x0 */ 546dacca5f0SHans Verkuil x_offset += x_int; 547dacca5f0SHans Verkuil x_err += x_fract; 548dacca5f0SHans Verkuil if (x_err > width) { 549dacca5f0SHans Verkuil x_offset++; 550dacca5f0SHans Verkuil x_err -= width; 551dacca5f0SHans Verkuil } 552dacca5f0SHans Verkuil 553dacca5f0SHans Verkuil if (ctx->mode & MEM2MEM_HFLIP) 554dacca5f0SHans Verkuil p_in_x[0] = p_line - x_offset * bytes_per_pixel; 555dacca5f0SHans Verkuil else 556dacca5f0SHans Verkuil p_in_x[0] = p_line + x_offset * bytes_per_pixel; 557dacca5f0SHans Verkuil } 558dacca5f0SHans Verkuil } 559dacca5f0SHans Verkuil 560dacca5f0SHans Verkuil return 0; 561dacca5f0SHans Verkuil } 562dacca5f0SHans Verkuil 563dacca5f0SHans Verkuil /* 564dacca5f0SHans Verkuil * mem2mem callbacks 565dacca5f0SHans Verkuil */ 566dacca5f0SHans Verkuil 567dacca5f0SHans Verkuil /* 568dacca5f0SHans Verkuil * job_ready() - check whether an instance is ready to be scheduled to run 569dacca5f0SHans Verkuil */ 570dacca5f0SHans Verkuil static int job_ready(void *priv) 571dacca5f0SHans Verkuil { 572dacca5f0SHans Verkuil struct vim2m_ctx *ctx = priv; 573dacca5f0SHans Verkuil 574dacca5f0SHans Verkuil if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen 575dacca5f0SHans Verkuil || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) { 576dacca5f0SHans Verkuil dprintk(ctx->dev, 1, "Not enough buffers available\n"); 577dacca5f0SHans Verkuil return 0; 578dacca5f0SHans Verkuil } 579dacca5f0SHans Verkuil 580dacca5f0SHans Verkuil return 1; 581dacca5f0SHans Verkuil } 582dacca5f0SHans Verkuil 583dacca5f0SHans Verkuil static void job_abort(void *priv) 584dacca5f0SHans Verkuil { 585dacca5f0SHans Verkuil struct vim2m_ctx *ctx = priv; 586dacca5f0SHans Verkuil 587dacca5f0SHans Verkuil /* Will cancel the transaction in the next interrupt handler */ 588dacca5f0SHans Verkuil ctx->aborting = 1; 589dacca5f0SHans Verkuil } 590dacca5f0SHans Verkuil 591dacca5f0SHans Verkuil /* device_run() - prepares and starts the device 592dacca5f0SHans Verkuil * 593dacca5f0SHans Verkuil * This simulates all the immediate preparations required before starting 594dacca5f0SHans Verkuil * a device. This will be called by the framework when it decides to schedule 595dacca5f0SHans Verkuil * a particular instance. 596dacca5f0SHans Verkuil */ 597dacca5f0SHans Verkuil static void device_run(void *priv) 598dacca5f0SHans Verkuil { 599dacca5f0SHans Verkuil struct vim2m_ctx *ctx = priv; 600dacca5f0SHans Verkuil struct vb2_v4l2_buffer *src_buf, *dst_buf; 601dacca5f0SHans Verkuil 602dacca5f0SHans Verkuil src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 603dacca5f0SHans Verkuil dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 604dacca5f0SHans Verkuil 605dacca5f0SHans Verkuil /* Apply request controls if any */ 606dacca5f0SHans Verkuil v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, 607dacca5f0SHans Verkuil &ctx->hdl); 608dacca5f0SHans Verkuil 609dacca5f0SHans Verkuil device_process(ctx, src_buf, dst_buf); 610dacca5f0SHans Verkuil 611dacca5f0SHans Verkuil /* Complete request controls if any */ 612dacca5f0SHans Verkuil v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, 613dacca5f0SHans Verkuil &ctx->hdl); 614dacca5f0SHans Verkuil 615dacca5f0SHans Verkuil /* Run delayed work, which simulates a hardware irq */ 616dacca5f0SHans Verkuil schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime)); 617dacca5f0SHans Verkuil } 618dacca5f0SHans Verkuil 619dacca5f0SHans Verkuil static void device_work(struct work_struct *w) 620dacca5f0SHans Verkuil { 621dacca5f0SHans Verkuil struct vim2m_ctx *curr_ctx; 622dacca5f0SHans Verkuil struct vim2m_dev *vim2m_dev; 623dacca5f0SHans Verkuil struct vb2_v4l2_buffer *src_vb, *dst_vb; 624dacca5f0SHans Verkuil 625dacca5f0SHans Verkuil curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); 626dacca5f0SHans Verkuil 627dacca5f0SHans Verkuil if (!curr_ctx) { 628dacca5f0SHans Verkuil pr_err("Instance released before the end of transaction\n"); 629dacca5f0SHans Verkuil return; 630dacca5f0SHans Verkuil } 631dacca5f0SHans Verkuil 632dacca5f0SHans Verkuil vim2m_dev = curr_ctx->dev; 633dacca5f0SHans Verkuil 634dacca5f0SHans Verkuil src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); 635dacca5f0SHans Verkuil dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); 636dacca5f0SHans Verkuil 637dacca5f0SHans Verkuil curr_ctx->num_processed++; 638dacca5f0SHans Verkuil 639dacca5f0SHans Verkuil v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); 640dacca5f0SHans Verkuil v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); 641dacca5f0SHans Verkuil 642dacca5f0SHans Verkuil if (curr_ctx->num_processed == curr_ctx->translen 643dacca5f0SHans Verkuil || curr_ctx->aborting) { 644dacca5f0SHans Verkuil dprintk(curr_ctx->dev, 2, "Finishing capture buffer fill\n"); 645dacca5f0SHans Verkuil curr_ctx->num_processed = 0; 646dacca5f0SHans Verkuil v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx); 647dacca5f0SHans Verkuil } else { 648dacca5f0SHans Verkuil device_run(curr_ctx); 649dacca5f0SHans Verkuil } 650dacca5f0SHans Verkuil } 651dacca5f0SHans Verkuil 652dacca5f0SHans Verkuil /* 653dacca5f0SHans Verkuil * video ioctls 654dacca5f0SHans Verkuil */ 655dacca5f0SHans Verkuil static int vidioc_querycap(struct file *file, void *priv, 656dacca5f0SHans Verkuil struct v4l2_capability *cap) 657dacca5f0SHans Verkuil { 658dacca5f0SHans Verkuil strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); 659dacca5f0SHans Verkuil strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); 660dacca5f0SHans Verkuil snprintf(cap->bus_info, sizeof(cap->bus_info), 661dacca5f0SHans Verkuil "platform:%s", MEM2MEM_NAME); 662dacca5f0SHans Verkuil return 0; 663dacca5f0SHans Verkuil } 664dacca5f0SHans Verkuil 665dacca5f0SHans Verkuil static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) 666dacca5f0SHans Verkuil { 667dacca5f0SHans Verkuil int i, num; 668dacca5f0SHans Verkuil struct vim2m_fmt *fmt; 669dacca5f0SHans Verkuil 670dacca5f0SHans Verkuil num = 0; 671dacca5f0SHans Verkuil 672dacca5f0SHans Verkuil for (i = 0; i < NUM_FORMATS; ++i) { 673dacca5f0SHans Verkuil if (formats[i].types & type) { 674dacca5f0SHans Verkuil /* index-th format of type type found ? */ 675dacca5f0SHans Verkuil if (num == f->index) 676dacca5f0SHans Verkuil break; 677dacca5f0SHans Verkuil /* 678dacca5f0SHans Verkuil * Correct type but haven't reached our index yet, 679dacca5f0SHans Verkuil * just increment per-type index 680dacca5f0SHans Verkuil */ 681dacca5f0SHans Verkuil ++num; 682dacca5f0SHans Verkuil } 683dacca5f0SHans Verkuil } 684dacca5f0SHans Verkuil 685dacca5f0SHans Verkuil if (i < NUM_FORMATS) { 686dacca5f0SHans Verkuil /* Format found */ 687dacca5f0SHans Verkuil fmt = &formats[i]; 688dacca5f0SHans Verkuil f->pixelformat = fmt->fourcc; 689dacca5f0SHans Verkuil return 0; 690dacca5f0SHans Verkuil } 691dacca5f0SHans Verkuil 692dacca5f0SHans Verkuil /* Format not found */ 693dacca5f0SHans Verkuil return -EINVAL; 694dacca5f0SHans Verkuil } 695dacca5f0SHans Verkuil 696dacca5f0SHans Verkuil static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 697dacca5f0SHans Verkuil struct v4l2_fmtdesc *f) 698dacca5f0SHans Verkuil { 699dacca5f0SHans Verkuil return enum_fmt(f, MEM2MEM_CAPTURE); 700dacca5f0SHans Verkuil } 701dacca5f0SHans Verkuil 702dacca5f0SHans Verkuil static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, 703dacca5f0SHans Verkuil struct v4l2_fmtdesc *f) 704dacca5f0SHans Verkuil { 705dacca5f0SHans Verkuil return enum_fmt(f, MEM2MEM_OUTPUT); 706dacca5f0SHans Verkuil } 707dacca5f0SHans Verkuil 708dacca5f0SHans Verkuil static int vidioc_enum_framesizes(struct file *file, void *priv, 709dacca5f0SHans Verkuil struct v4l2_frmsizeenum *fsize) 710dacca5f0SHans Verkuil { 711dacca5f0SHans Verkuil if (fsize->index != 0) 712dacca5f0SHans Verkuil return -EINVAL; 713dacca5f0SHans Verkuil 714dacca5f0SHans Verkuil if (!find_format(fsize->pixel_format)) 715dacca5f0SHans Verkuil return -EINVAL; 716dacca5f0SHans Verkuil 717dacca5f0SHans Verkuil fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 718dacca5f0SHans Verkuil fsize->stepwise.min_width = MIN_W; 719dacca5f0SHans Verkuil fsize->stepwise.min_height = MIN_H; 720dacca5f0SHans Verkuil fsize->stepwise.max_width = MAX_W; 721dacca5f0SHans Verkuil fsize->stepwise.max_height = MAX_H; 722dacca5f0SHans Verkuil 723dacca5f0SHans Verkuil get_alignment(fsize->pixel_format, 724dacca5f0SHans Verkuil &fsize->stepwise.step_width, 725dacca5f0SHans Verkuil &fsize->stepwise.step_height); 726dacca5f0SHans Verkuil return 0; 727dacca5f0SHans Verkuil } 728dacca5f0SHans Verkuil 729dacca5f0SHans Verkuil static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) 730dacca5f0SHans Verkuil { 731dacca5f0SHans Verkuil struct vb2_queue *vq; 732dacca5f0SHans Verkuil struct vim2m_q_data *q_data; 733dacca5f0SHans Verkuil 734dacca5f0SHans Verkuil vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 735dacca5f0SHans Verkuil if (!vq) 736dacca5f0SHans Verkuil return -EINVAL; 737dacca5f0SHans Verkuil 738dacca5f0SHans Verkuil q_data = get_q_data(ctx, f->type); 739dacca5f0SHans Verkuil if (!q_data) 740dacca5f0SHans Verkuil return -EINVAL; 741dacca5f0SHans Verkuil 742dacca5f0SHans Verkuil f->fmt.pix.width = q_data->width; 743dacca5f0SHans Verkuil f->fmt.pix.height = q_data->height; 744dacca5f0SHans Verkuil f->fmt.pix.field = V4L2_FIELD_NONE; 745dacca5f0SHans Verkuil f->fmt.pix.pixelformat = q_data->fmt->fourcc; 746dacca5f0SHans Verkuil f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; 747dacca5f0SHans Verkuil f->fmt.pix.sizeimage = q_data->sizeimage; 748dacca5f0SHans Verkuil f->fmt.pix.colorspace = ctx->colorspace; 749dacca5f0SHans Verkuil f->fmt.pix.xfer_func = ctx->xfer_func; 750dacca5f0SHans Verkuil f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; 751dacca5f0SHans Verkuil f->fmt.pix.quantization = ctx->quant; 752dacca5f0SHans Verkuil 753dacca5f0SHans Verkuil return 0; 754dacca5f0SHans Verkuil } 755dacca5f0SHans Verkuil 756dacca5f0SHans Verkuil static int vidioc_g_fmt_vid_out(struct file *file, void *priv, 757dacca5f0SHans Verkuil struct v4l2_format *f) 758dacca5f0SHans Verkuil { 759dacca5f0SHans Verkuil return vidioc_g_fmt(file2ctx(file), f); 760dacca5f0SHans Verkuil } 761dacca5f0SHans Verkuil 762dacca5f0SHans Verkuil static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 763dacca5f0SHans Verkuil struct v4l2_format *f) 764dacca5f0SHans Verkuil { 765dacca5f0SHans Verkuil return vidioc_g_fmt(file2ctx(file), f); 766dacca5f0SHans Verkuil } 767dacca5f0SHans Verkuil 768dacca5f0SHans Verkuil static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt) 769dacca5f0SHans Verkuil { 770dacca5f0SHans Verkuil int walign, halign; 771dacca5f0SHans Verkuil /* 772dacca5f0SHans Verkuil * V4L2 specification specifies the driver corrects the 773dacca5f0SHans Verkuil * format struct if any of the dimensions is unsupported 774dacca5f0SHans Verkuil */ 775dacca5f0SHans Verkuil if (f->fmt.pix.height < MIN_H) 776dacca5f0SHans Verkuil f->fmt.pix.height = MIN_H; 777dacca5f0SHans Verkuil else if (f->fmt.pix.height > MAX_H) 778dacca5f0SHans Verkuil f->fmt.pix.height = MAX_H; 779dacca5f0SHans Verkuil 780dacca5f0SHans Verkuil if (f->fmt.pix.width < MIN_W) 781dacca5f0SHans Verkuil f->fmt.pix.width = MIN_W; 782dacca5f0SHans Verkuil else if (f->fmt.pix.width > MAX_W) 783dacca5f0SHans Verkuil f->fmt.pix.width = MAX_W; 784dacca5f0SHans Verkuil 785dacca5f0SHans Verkuil get_alignment(f->fmt.pix.pixelformat, &walign, &halign); 786dacca5f0SHans Verkuil f->fmt.pix.width &= ~(walign - 1); 787dacca5f0SHans Verkuil f->fmt.pix.height &= ~(halign - 1); 788dacca5f0SHans Verkuil f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 789dacca5f0SHans Verkuil f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 790dacca5f0SHans Verkuil f->fmt.pix.field = V4L2_FIELD_NONE; 791dacca5f0SHans Verkuil 792dacca5f0SHans Verkuil return 0; 793dacca5f0SHans Verkuil } 794dacca5f0SHans Verkuil 795dacca5f0SHans Verkuil static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 796dacca5f0SHans Verkuil struct v4l2_format *f) 797dacca5f0SHans Verkuil { 798dacca5f0SHans Verkuil struct vim2m_fmt *fmt; 799dacca5f0SHans Verkuil struct vim2m_ctx *ctx = file2ctx(file); 800dacca5f0SHans Verkuil 801dacca5f0SHans Verkuil fmt = find_format(f->fmt.pix.pixelformat); 802dacca5f0SHans Verkuil if (!fmt) { 803dacca5f0SHans Verkuil f->fmt.pix.pixelformat = formats[0].fourcc; 804dacca5f0SHans Verkuil fmt = find_format(f->fmt.pix.pixelformat); 805dacca5f0SHans Verkuil } 806dacca5f0SHans Verkuil if (!(fmt->types & MEM2MEM_CAPTURE)) { 807dacca5f0SHans Verkuil v4l2_err(&ctx->dev->v4l2_dev, 808dacca5f0SHans Verkuil "Fourcc format (0x%08x) invalid.\n", 809dacca5f0SHans Verkuil f->fmt.pix.pixelformat); 810dacca5f0SHans Verkuil return -EINVAL; 811dacca5f0SHans Verkuil } 812dacca5f0SHans Verkuil f->fmt.pix.colorspace = ctx->colorspace; 813dacca5f0SHans Verkuil f->fmt.pix.xfer_func = ctx->xfer_func; 814dacca5f0SHans Verkuil f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; 815dacca5f0SHans Verkuil f->fmt.pix.quantization = ctx->quant; 816dacca5f0SHans Verkuil 817dacca5f0SHans Verkuil return vidioc_try_fmt(f, fmt); 818dacca5f0SHans Verkuil } 819dacca5f0SHans Verkuil 820dacca5f0SHans Verkuil static int vidioc_try_fmt_vid_out(struct file *file, void *priv, 821dacca5f0SHans Verkuil struct v4l2_format *f) 822dacca5f0SHans Verkuil { 823dacca5f0SHans Verkuil struct vim2m_fmt *fmt; 824dacca5f0SHans Verkuil struct vim2m_ctx *ctx = file2ctx(file); 825dacca5f0SHans Verkuil 826dacca5f0SHans Verkuil fmt = find_format(f->fmt.pix.pixelformat); 827dacca5f0SHans Verkuil if (!fmt) { 828dacca5f0SHans Verkuil f->fmt.pix.pixelformat = formats[0].fourcc; 829dacca5f0SHans Verkuil fmt = find_format(f->fmt.pix.pixelformat); 830dacca5f0SHans Verkuil } 831dacca5f0SHans Verkuil if (!(fmt->types & MEM2MEM_OUTPUT)) { 832dacca5f0SHans Verkuil v4l2_err(&ctx->dev->v4l2_dev, 833dacca5f0SHans Verkuil "Fourcc format (0x%08x) invalid.\n", 834dacca5f0SHans Verkuil f->fmt.pix.pixelformat); 835dacca5f0SHans Verkuil return -EINVAL; 836dacca5f0SHans Verkuil } 837dacca5f0SHans Verkuil if (!f->fmt.pix.colorspace) 838dacca5f0SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; 839dacca5f0SHans Verkuil 840dacca5f0SHans Verkuil return vidioc_try_fmt(f, fmt); 841dacca5f0SHans Verkuil } 842dacca5f0SHans Verkuil 843dacca5f0SHans Verkuil static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) 844dacca5f0SHans Verkuil { 845dacca5f0SHans Verkuil struct vim2m_q_data *q_data; 846dacca5f0SHans Verkuil struct vb2_queue *vq; 847dacca5f0SHans Verkuil 848dacca5f0SHans Verkuil vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 849dacca5f0SHans Verkuil if (!vq) 850dacca5f0SHans Verkuil return -EINVAL; 851dacca5f0SHans Verkuil 852dacca5f0SHans Verkuil q_data = get_q_data(ctx, f->type); 853dacca5f0SHans Verkuil if (!q_data) 854dacca5f0SHans Verkuil return -EINVAL; 855dacca5f0SHans Verkuil 856dacca5f0SHans Verkuil if (vb2_is_busy(vq)) { 857dacca5f0SHans Verkuil v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); 858dacca5f0SHans Verkuil return -EBUSY; 859dacca5f0SHans Verkuil } 860dacca5f0SHans Verkuil 861dacca5f0SHans Verkuil q_data->fmt = find_format(f->fmt.pix.pixelformat); 862dacca5f0SHans Verkuil q_data->width = f->fmt.pix.width; 863dacca5f0SHans Verkuil q_data->height = f->fmt.pix.height; 864dacca5f0SHans Verkuil q_data->sizeimage = q_data->width * q_data->height 865dacca5f0SHans Verkuil * q_data->fmt->depth >> 3; 866dacca5f0SHans Verkuil 867dacca5f0SHans Verkuil dprintk(ctx->dev, 1, 868dacca5f0SHans Verkuil "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n", 869dacca5f0SHans Verkuil type_name(f->type), q_data->width, q_data->height, 870dacca5f0SHans Verkuil q_data->fmt->depth, 871dacca5f0SHans Verkuil (q_data->fmt->fourcc & 0xff), 872dacca5f0SHans Verkuil (q_data->fmt->fourcc >> 8) & 0xff, 873dacca5f0SHans Verkuil (q_data->fmt->fourcc >> 16) & 0xff, 874dacca5f0SHans Verkuil (q_data->fmt->fourcc >> 24) & 0xff); 875dacca5f0SHans Verkuil 876dacca5f0SHans Verkuil return 0; 877dacca5f0SHans Verkuil } 878dacca5f0SHans Verkuil 879dacca5f0SHans Verkuil static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 880dacca5f0SHans Verkuil struct v4l2_format *f) 881dacca5f0SHans Verkuil { 882dacca5f0SHans Verkuil int ret; 883dacca5f0SHans Verkuil 884dacca5f0SHans Verkuil ret = vidioc_try_fmt_vid_cap(file, priv, f); 885dacca5f0SHans Verkuil if (ret) 886dacca5f0SHans Verkuil return ret; 887dacca5f0SHans Verkuil 888dacca5f0SHans Verkuil return vidioc_s_fmt(file2ctx(file), f); 889dacca5f0SHans Verkuil } 890dacca5f0SHans Verkuil 891dacca5f0SHans Verkuil static int vidioc_s_fmt_vid_out(struct file *file, void *priv, 892dacca5f0SHans Verkuil struct v4l2_format *f) 893dacca5f0SHans Verkuil { 894dacca5f0SHans Verkuil struct vim2m_ctx *ctx = file2ctx(file); 895dacca5f0SHans Verkuil int ret; 896dacca5f0SHans Verkuil 897dacca5f0SHans Verkuil ret = vidioc_try_fmt_vid_out(file, priv, f); 898dacca5f0SHans Verkuil if (ret) 899dacca5f0SHans Verkuil return ret; 900dacca5f0SHans Verkuil 901dacca5f0SHans Verkuil ret = vidioc_s_fmt(file2ctx(file), f); 902dacca5f0SHans Verkuil if (!ret) { 903dacca5f0SHans Verkuil ctx->colorspace = f->fmt.pix.colorspace; 904dacca5f0SHans Verkuil ctx->xfer_func = f->fmt.pix.xfer_func; 905dacca5f0SHans Verkuil ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; 906dacca5f0SHans Verkuil ctx->quant = f->fmt.pix.quantization; 907dacca5f0SHans Verkuil } 908dacca5f0SHans Verkuil return ret; 909dacca5f0SHans Verkuil } 910dacca5f0SHans Verkuil 911dacca5f0SHans Verkuil static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) 912dacca5f0SHans Verkuil { 913dacca5f0SHans Verkuil struct vim2m_ctx *ctx = 914dacca5f0SHans Verkuil container_of(ctrl->handler, struct vim2m_ctx, hdl); 915dacca5f0SHans Verkuil 916dacca5f0SHans Verkuil switch (ctrl->id) { 917dacca5f0SHans Verkuil case V4L2_CID_HFLIP: 918dacca5f0SHans Verkuil if (ctrl->val) 919dacca5f0SHans Verkuil ctx->mode |= MEM2MEM_HFLIP; 920dacca5f0SHans Verkuil else 921dacca5f0SHans Verkuil ctx->mode &= ~MEM2MEM_HFLIP; 922dacca5f0SHans Verkuil break; 923dacca5f0SHans Verkuil 924dacca5f0SHans Verkuil case V4L2_CID_VFLIP: 925dacca5f0SHans Verkuil if (ctrl->val) 926dacca5f0SHans Verkuil ctx->mode |= MEM2MEM_VFLIP; 927dacca5f0SHans Verkuil else 928dacca5f0SHans Verkuil ctx->mode &= ~MEM2MEM_VFLIP; 929dacca5f0SHans Verkuil break; 930dacca5f0SHans Verkuil 931dacca5f0SHans Verkuil case V4L2_CID_TRANS_TIME_MSEC: 932dacca5f0SHans Verkuil ctx->transtime = ctrl->val; 933dacca5f0SHans Verkuil if (ctx->transtime < 1) 934dacca5f0SHans Verkuil ctx->transtime = 1; 935dacca5f0SHans Verkuil break; 936dacca5f0SHans Verkuil 937dacca5f0SHans Verkuil case V4L2_CID_TRANS_NUM_BUFS: 938dacca5f0SHans Verkuil ctx->translen = ctrl->val; 939dacca5f0SHans Verkuil break; 940dacca5f0SHans Verkuil 941dacca5f0SHans Verkuil default: 942dacca5f0SHans Verkuil v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); 943dacca5f0SHans Verkuil return -EINVAL; 944dacca5f0SHans Verkuil } 945dacca5f0SHans Verkuil 946dacca5f0SHans Verkuil return 0; 947dacca5f0SHans Verkuil } 948dacca5f0SHans Verkuil 949dacca5f0SHans Verkuil static const struct v4l2_ctrl_ops vim2m_ctrl_ops = { 950dacca5f0SHans Verkuil .s_ctrl = vim2m_s_ctrl, 951dacca5f0SHans Verkuil }; 952dacca5f0SHans Verkuil 953dacca5f0SHans Verkuil static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { 954dacca5f0SHans Verkuil .vidioc_querycap = vidioc_querycap, 955dacca5f0SHans Verkuil 956dacca5f0SHans Verkuil .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 957dacca5f0SHans Verkuil .vidioc_enum_framesizes = vidioc_enum_framesizes, 958dacca5f0SHans Verkuil .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 959dacca5f0SHans Verkuil .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 960dacca5f0SHans Verkuil .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 961dacca5f0SHans Verkuil 962dacca5f0SHans Verkuil .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, 963dacca5f0SHans Verkuil .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, 964dacca5f0SHans Verkuil .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, 965dacca5f0SHans Verkuil .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, 966dacca5f0SHans Verkuil 967dacca5f0SHans Verkuil .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 968dacca5f0SHans Verkuil .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 969dacca5f0SHans Verkuil .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 970dacca5f0SHans Verkuil .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 971dacca5f0SHans Verkuil .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 972dacca5f0SHans Verkuil .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 973dacca5f0SHans Verkuil .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 974dacca5f0SHans Verkuil 975dacca5f0SHans Verkuil .vidioc_streamon = v4l2_m2m_ioctl_streamon, 976dacca5f0SHans Verkuil .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 977dacca5f0SHans Verkuil 978dacca5f0SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 979dacca5f0SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 980dacca5f0SHans Verkuil }; 981dacca5f0SHans Verkuil 982dacca5f0SHans Verkuil /* 983dacca5f0SHans Verkuil * Queue operations 984dacca5f0SHans Verkuil */ 985dacca5f0SHans Verkuil 986dacca5f0SHans Verkuil static int vim2m_queue_setup(struct vb2_queue *vq, 987dacca5f0SHans Verkuil unsigned int *nbuffers, 988dacca5f0SHans Verkuil unsigned int *nplanes, 989dacca5f0SHans Verkuil unsigned int sizes[], 990dacca5f0SHans Verkuil struct device *alloc_devs[]) 991dacca5f0SHans Verkuil { 992dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); 993dacca5f0SHans Verkuil struct vim2m_q_data *q_data; 994dacca5f0SHans Verkuil unsigned int size, count = *nbuffers; 995dacca5f0SHans Verkuil 996dacca5f0SHans Verkuil q_data = get_q_data(ctx, vq->type); 997dacca5f0SHans Verkuil if (!q_data) 998dacca5f0SHans Verkuil return -EINVAL; 999dacca5f0SHans Verkuil 1000dacca5f0SHans Verkuil size = q_data->width * q_data->height * q_data->fmt->depth >> 3; 1001dacca5f0SHans Verkuil 1002dacca5f0SHans Verkuil while (size * count > MEM2MEM_VID_MEM_LIMIT) 1003dacca5f0SHans Verkuil (count)--; 1004dacca5f0SHans Verkuil *nbuffers = count; 1005dacca5f0SHans Verkuil 1006dacca5f0SHans Verkuil if (*nplanes) 1007dacca5f0SHans Verkuil return sizes[0] < size ? -EINVAL : 0; 1008dacca5f0SHans Verkuil 1009dacca5f0SHans Verkuil *nplanes = 1; 1010dacca5f0SHans Verkuil sizes[0] = size; 1011dacca5f0SHans Verkuil 1012dacca5f0SHans Verkuil dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n", 1013dacca5f0SHans Verkuil type_name(vq->type), count, size); 1014dacca5f0SHans Verkuil 1015dacca5f0SHans Verkuil return 0; 1016dacca5f0SHans Verkuil } 1017dacca5f0SHans Verkuil 1018dacca5f0SHans Verkuil static int vim2m_buf_out_validate(struct vb2_buffer *vb) 1019dacca5f0SHans Verkuil { 1020dacca5f0SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1021dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1022dacca5f0SHans Verkuil 1023dacca5f0SHans Verkuil if (vbuf->field == V4L2_FIELD_ANY) 1024dacca5f0SHans Verkuil vbuf->field = V4L2_FIELD_NONE; 1025dacca5f0SHans Verkuil if (vbuf->field != V4L2_FIELD_NONE) { 1026dacca5f0SHans Verkuil dprintk(ctx->dev, 1, "%s field isn't supported\n", __func__); 1027dacca5f0SHans Verkuil return -EINVAL; 1028dacca5f0SHans Verkuil } 1029dacca5f0SHans Verkuil 1030dacca5f0SHans Verkuil return 0; 1031dacca5f0SHans Verkuil } 1032dacca5f0SHans Verkuil 1033dacca5f0SHans Verkuil static int vim2m_buf_prepare(struct vb2_buffer *vb) 1034dacca5f0SHans Verkuil { 1035dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1036dacca5f0SHans Verkuil struct vim2m_q_data *q_data; 1037dacca5f0SHans Verkuil 1038dacca5f0SHans Verkuil dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type)); 1039dacca5f0SHans Verkuil 1040dacca5f0SHans Verkuil q_data = get_q_data(ctx, vb->vb2_queue->type); 1041dacca5f0SHans Verkuil if (!q_data) 1042dacca5f0SHans Verkuil return -EINVAL; 1043dacca5f0SHans Verkuil if (vb2_plane_size(vb, 0) < q_data->sizeimage) { 1044dacca5f0SHans Verkuil dprintk(ctx->dev, 1, 1045dacca5f0SHans Verkuil "%s data will not fit into plane (%lu < %lu)\n", 1046dacca5f0SHans Verkuil __func__, vb2_plane_size(vb, 0), 1047dacca5f0SHans Verkuil (long)q_data->sizeimage); 1048dacca5f0SHans Verkuil return -EINVAL; 1049dacca5f0SHans Verkuil } 1050dacca5f0SHans Verkuil 1051dacca5f0SHans Verkuil vb2_set_plane_payload(vb, 0, q_data->sizeimage); 1052dacca5f0SHans Verkuil 1053dacca5f0SHans Verkuil return 0; 1054dacca5f0SHans Verkuil } 1055dacca5f0SHans Verkuil 1056dacca5f0SHans Verkuil static void vim2m_buf_queue(struct vb2_buffer *vb) 1057dacca5f0SHans Verkuil { 1058dacca5f0SHans Verkuil struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1059dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1060dacca5f0SHans Verkuil 1061dacca5f0SHans Verkuil v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 1062dacca5f0SHans Verkuil } 1063dacca5f0SHans Verkuil 1064dacca5f0SHans Verkuil static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count) 1065dacca5f0SHans Verkuil { 1066dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(q); 1067dacca5f0SHans Verkuil struct vim2m_q_data *q_data = get_q_data(ctx, q->type); 1068dacca5f0SHans Verkuil 1069dacca5f0SHans Verkuil if (!q_data) 1070dacca5f0SHans Verkuil return -EINVAL; 1071dacca5f0SHans Verkuil 1072dacca5f0SHans Verkuil if (V4L2_TYPE_IS_OUTPUT(q->type)) 1073dacca5f0SHans Verkuil ctx->aborting = 0; 1074dacca5f0SHans Verkuil 1075dacca5f0SHans Verkuil q_data->sequence = 0; 1076dacca5f0SHans Verkuil return 0; 1077dacca5f0SHans Verkuil } 1078dacca5f0SHans Verkuil 1079dacca5f0SHans Verkuil static void vim2m_stop_streaming(struct vb2_queue *q) 1080dacca5f0SHans Verkuil { 1081dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(q); 1082dacca5f0SHans Verkuil struct vb2_v4l2_buffer *vbuf; 1083dacca5f0SHans Verkuil 1084dacca5f0SHans Verkuil cancel_delayed_work_sync(&ctx->work_run); 1085dacca5f0SHans Verkuil 1086dacca5f0SHans Verkuil for (;;) { 1087dacca5f0SHans Verkuil if (V4L2_TYPE_IS_OUTPUT(q->type)) 1088dacca5f0SHans Verkuil vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 1089dacca5f0SHans Verkuil else 1090dacca5f0SHans Verkuil vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 1091dacca5f0SHans Verkuil if (!vbuf) 1092dacca5f0SHans Verkuil return; 1093dacca5f0SHans Verkuil v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, 1094dacca5f0SHans Verkuil &ctx->hdl); 1095dacca5f0SHans Verkuil v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); 1096dacca5f0SHans Verkuil } 1097dacca5f0SHans Verkuil } 1098dacca5f0SHans Verkuil 1099dacca5f0SHans Verkuil static void vim2m_buf_request_complete(struct vb2_buffer *vb) 1100dacca5f0SHans Verkuil { 1101dacca5f0SHans Verkuil struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1102dacca5f0SHans Verkuil 1103dacca5f0SHans Verkuil v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); 1104dacca5f0SHans Verkuil } 1105dacca5f0SHans Verkuil 1106dacca5f0SHans Verkuil static const struct vb2_ops vim2m_qops = { 1107dacca5f0SHans Verkuil .queue_setup = vim2m_queue_setup, 1108dacca5f0SHans Verkuil .buf_out_validate = vim2m_buf_out_validate, 1109dacca5f0SHans Verkuil .buf_prepare = vim2m_buf_prepare, 1110dacca5f0SHans Verkuil .buf_queue = vim2m_buf_queue, 1111dacca5f0SHans Verkuil .start_streaming = vim2m_start_streaming, 1112dacca5f0SHans Verkuil .stop_streaming = vim2m_stop_streaming, 1113dacca5f0SHans Verkuil .wait_prepare = vb2_ops_wait_prepare, 1114dacca5f0SHans Verkuil .wait_finish = vb2_ops_wait_finish, 1115dacca5f0SHans Verkuil .buf_request_complete = vim2m_buf_request_complete, 1116dacca5f0SHans Verkuil }; 1117dacca5f0SHans Verkuil 1118dacca5f0SHans Verkuil static int queue_init(void *priv, struct vb2_queue *src_vq, 1119dacca5f0SHans Verkuil struct vb2_queue *dst_vq) 1120dacca5f0SHans Verkuil { 1121dacca5f0SHans Verkuil struct vim2m_ctx *ctx = priv; 1122dacca5f0SHans Verkuil int ret; 1123dacca5f0SHans Verkuil 1124dacca5f0SHans Verkuil src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1125dacca5f0SHans Verkuil src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 1126dacca5f0SHans Verkuil src_vq->drv_priv = ctx; 1127dacca5f0SHans Verkuil src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 1128dacca5f0SHans Verkuil src_vq->ops = &vim2m_qops; 1129dacca5f0SHans Verkuil src_vq->mem_ops = &vb2_vmalloc_memops; 1130dacca5f0SHans Verkuil src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 1131dacca5f0SHans Verkuil src_vq->lock = &ctx->vb_mutex; 1132dacca5f0SHans Verkuil src_vq->supports_requests = true; 1133dacca5f0SHans Verkuil 1134dacca5f0SHans Verkuil ret = vb2_queue_init(src_vq); 1135dacca5f0SHans Verkuil if (ret) 1136dacca5f0SHans Verkuil return ret; 1137dacca5f0SHans Verkuil 1138dacca5f0SHans Verkuil dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1139dacca5f0SHans Verkuil dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 1140dacca5f0SHans Verkuil dst_vq->drv_priv = ctx; 1141dacca5f0SHans Verkuil dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 1142dacca5f0SHans Verkuil dst_vq->ops = &vim2m_qops; 1143dacca5f0SHans Verkuil dst_vq->mem_ops = &vb2_vmalloc_memops; 1144dacca5f0SHans Verkuil dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 1145dacca5f0SHans Verkuil dst_vq->lock = &ctx->vb_mutex; 1146dacca5f0SHans Verkuil 1147dacca5f0SHans Verkuil return vb2_queue_init(dst_vq); 1148dacca5f0SHans Verkuil } 1149dacca5f0SHans Verkuil 1150dacca5f0SHans Verkuil static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = { 1151dacca5f0SHans Verkuil .ops = &vim2m_ctrl_ops, 1152dacca5f0SHans Verkuil .id = V4L2_CID_TRANS_TIME_MSEC, 1153dacca5f0SHans Verkuil .name = "Transaction Time (msec)", 1154dacca5f0SHans Verkuil .type = V4L2_CTRL_TYPE_INTEGER, 1155dacca5f0SHans Verkuil .min = 1, 1156dacca5f0SHans Verkuil .max = 10001, 1157dacca5f0SHans Verkuil .step = 1, 1158dacca5f0SHans Verkuil }; 1159dacca5f0SHans Verkuil 1160dacca5f0SHans Verkuil static const struct v4l2_ctrl_config vim2m_ctrl_trans_num_bufs = { 1161dacca5f0SHans Verkuil .ops = &vim2m_ctrl_ops, 1162dacca5f0SHans Verkuil .id = V4L2_CID_TRANS_NUM_BUFS, 1163dacca5f0SHans Verkuil .name = "Buffers Per Transaction", 1164dacca5f0SHans Verkuil .type = V4L2_CTRL_TYPE_INTEGER, 1165dacca5f0SHans Verkuil .def = 1, 1166dacca5f0SHans Verkuil .min = 1, 1167dacca5f0SHans Verkuil .max = MEM2MEM_DEF_NUM_BUFS, 1168dacca5f0SHans Verkuil .step = 1, 1169dacca5f0SHans Verkuil }; 1170dacca5f0SHans Verkuil 1171dacca5f0SHans Verkuil /* 1172dacca5f0SHans Verkuil * File operations 1173dacca5f0SHans Verkuil */ 1174dacca5f0SHans Verkuil static int vim2m_open(struct file *file) 1175dacca5f0SHans Verkuil { 1176dacca5f0SHans Verkuil struct vim2m_dev *dev = video_drvdata(file); 1177dacca5f0SHans Verkuil struct vim2m_ctx *ctx = NULL; 1178dacca5f0SHans Verkuil struct v4l2_ctrl_handler *hdl; 1179dacca5f0SHans Verkuil int rc = 0; 1180dacca5f0SHans Verkuil 1181dacca5f0SHans Verkuil if (mutex_lock_interruptible(&dev->dev_mutex)) 1182dacca5f0SHans Verkuil return -ERESTARTSYS; 1183dacca5f0SHans Verkuil ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 1184dacca5f0SHans Verkuil if (!ctx) { 1185dacca5f0SHans Verkuil rc = -ENOMEM; 1186dacca5f0SHans Verkuil goto open_unlock; 1187dacca5f0SHans Verkuil } 1188dacca5f0SHans Verkuil 1189dacca5f0SHans Verkuil v4l2_fh_init(&ctx->fh, video_devdata(file)); 1190dacca5f0SHans Verkuil file->private_data = &ctx->fh; 1191dacca5f0SHans Verkuil ctx->dev = dev; 1192dacca5f0SHans Verkuil hdl = &ctx->hdl; 1193dacca5f0SHans Verkuil v4l2_ctrl_handler_init(hdl, 4); 1194dacca5f0SHans Verkuil v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 1195dacca5f0SHans Verkuil v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 1196dacca5f0SHans Verkuil 1197dacca5f0SHans Verkuil vim2m_ctrl_trans_time_msec.def = default_transtime; 1198dacca5f0SHans Verkuil v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL); 1199dacca5f0SHans Verkuil v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL); 1200dacca5f0SHans Verkuil if (hdl->error) { 1201dacca5f0SHans Verkuil rc = hdl->error; 1202dacca5f0SHans Verkuil v4l2_ctrl_handler_free(hdl); 1203dacca5f0SHans Verkuil kfree(ctx); 1204dacca5f0SHans Verkuil goto open_unlock; 1205dacca5f0SHans Verkuil } 1206dacca5f0SHans Verkuil ctx->fh.ctrl_handler = hdl; 1207dacca5f0SHans Verkuil v4l2_ctrl_handler_setup(hdl); 1208dacca5f0SHans Verkuil 1209dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; 1210dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].width = 640; 1211dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].height = 480; 1212dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].sizeimage = 1213dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].width * 1214dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_SRC].height * 1215dacca5f0SHans Verkuil (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); 1216dacca5f0SHans Verkuil ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; 1217dacca5f0SHans Verkuil ctx->colorspace = V4L2_COLORSPACE_REC709; 1218dacca5f0SHans Verkuil 1219dacca5f0SHans Verkuil ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); 1220dacca5f0SHans Verkuil 1221dacca5f0SHans Verkuil mutex_init(&ctx->vb_mutex); 1222dacca5f0SHans Verkuil INIT_DELAYED_WORK(&ctx->work_run, device_work); 1223dacca5f0SHans Verkuil 1224dacca5f0SHans Verkuil if (IS_ERR(ctx->fh.m2m_ctx)) { 1225dacca5f0SHans Verkuil rc = PTR_ERR(ctx->fh.m2m_ctx); 1226dacca5f0SHans Verkuil 1227dacca5f0SHans Verkuil v4l2_ctrl_handler_free(hdl); 1228dacca5f0SHans Verkuil v4l2_fh_exit(&ctx->fh); 1229dacca5f0SHans Verkuil kfree(ctx); 1230dacca5f0SHans Verkuil goto open_unlock; 1231dacca5f0SHans Verkuil } 1232dacca5f0SHans Verkuil 1233dacca5f0SHans Verkuil v4l2_fh_add(&ctx->fh); 1234dacca5f0SHans Verkuil atomic_inc(&dev->num_inst); 1235dacca5f0SHans Verkuil 1236dacca5f0SHans Verkuil dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n", 1237dacca5f0SHans Verkuil ctx, ctx->fh.m2m_ctx); 1238dacca5f0SHans Verkuil 1239dacca5f0SHans Verkuil open_unlock: 1240dacca5f0SHans Verkuil mutex_unlock(&dev->dev_mutex); 1241dacca5f0SHans Verkuil return rc; 1242dacca5f0SHans Verkuil } 1243dacca5f0SHans Verkuil 1244dacca5f0SHans Verkuil static int vim2m_release(struct file *file) 1245dacca5f0SHans Verkuil { 1246dacca5f0SHans Verkuil struct vim2m_dev *dev = video_drvdata(file); 1247dacca5f0SHans Verkuil struct vim2m_ctx *ctx = file2ctx(file); 1248dacca5f0SHans Verkuil 1249dacca5f0SHans Verkuil dprintk(dev, 1, "Releasing instance %p\n", ctx); 1250dacca5f0SHans Verkuil 1251dacca5f0SHans Verkuil v4l2_fh_del(&ctx->fh); 1252dacca5f0SHans Verkuil v4l2_fh_exit(&ctx->fh); 1253dacca5f0SHans Verkuil v4l2_ctrl_handler_free(&ctx->hdl); 1254dacca5f0SHans Verkuil mutex_lock(&dev->dev_mutex); 1255dacca5f0SHans Verkuil v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 1256dacca5f0SHans Verkuil mutex_unlock(&dev->dev_mutex); 1257dacca5f0SHans Verkuil kfree(ctx); 1258dacca5f0SHans Verkuil 1259dacca5f0SHans Verkuil atomic_dec(&dev->num_inst); 1260dacca5f0SHans Verkuil 1261dacca5f0SHans Verkuil return 0; 1262dacca5f0SHans Verkuil } 1263dacca5f0SHans Verkuil 1264dacca5f0SHans Verkuil static void vim2m_device_release(struct video_device *vdev) 1265dacca5f0SHans Verkuil { 1266dacca5f0SHans Verkuil struct vim2m_dev *dev = container_of(vdev, struct vim2m_dev, vfd); 1267dacca5f0SHans Verkuil 1268dacca5f0SHans Verkuil v4l2_device_unregister(&dev->v4l2_dev); 1269dacca5f0SHans Verkuil v4l2_m2m_release(dev->m2m_dev); 1270dacca5f0SHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 1271dacca5f0SHans Verkuil media_device_cleanup(&dev->mdev); 1272dacca5f0SHans Verkuil #endif 1273dacca5f0SHans Verkuil kfree(dev); 1274dacca5f0SHans Verkuil } 1275dacca5f0SHans Verkuil 1276dacca5f0SHans Verkuil static const struct v4l2_file_operations vim2m_fops = { 1277dacca5f0SHans Verkuil .owner = THIS_MODULE, 1278dacca5f0SHans Verkuil .open = vim2m_open, 1279dacca5f0SHans Verkuil .release = vim2m_release, 1280dacca5f0SHans Verkuil .poll = v4l2_m2m_fop_poll, 1281dacca5f0SHans Verkuil .unlocked_ioctl = video_ioctl2, 1282dacca5f0SHans Verkuil .mmap = v4l2_m2m_fop_mmap, 1283dacca5f0SHans Verkuil }; 1284dacca5f0SHans Verkuil 1285dacca5f0SHans Verkuil static const struct video_device vim2m_videodev = { 1286dacca5f0SHans Verkuil .name = MEM2MEM_NAME, 1287dacca5f0SHans Verkuil .vfl_dir = VFL_DIR_M2M, 1288dacca5f0SHans Verkuil .fops = &vim2m_fops, 1289dacca5f0SHans Verkuil .ioctl_ops = &vim2m_ioctl_ops, 1290dacca5f0SHans Verkuil .minor = -1, 1291dacca5f0SHans Verkuil .release = vim2m_device_release, 1292dacca5f0SHans Verkuil .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 1293dacca5f0SHans Verkuil }; 1294dacca5f0SHans Verkuil 1295dacca5f0SHans Verkuil static const struct v4l2_m2m_ops m2m_ops = { 1296dacca5f0SHans Verkuil .device_run = device_run, 1297dacca5f0SHans Verkuil .job_ready = job_ready, 1298dacca5f0SHans Verkuil .job_abort = job_abort, 1299dacca5f0SHans Verkuil }; 1300dacca5f0SHans Verkuil 1301dacca5f0SHans Verkuil static const struct media_device_ops m2m_media_ops = { 1302dacca5f0SHans Verkuil .req_validate = vb2_request_validate, 1303dacca5f0SHans Verkuil .req_queue = v4l2_m2m_request_queue, 1304dacca5f0SHans Verkuil }; 1305dacca5f0SHans Verkuil 1306dacca5f0SHans Verkuil static int vim2m_probe(struct platform_device *pdev) 1307dacca5f0SHans Verkuil { 1308dacca5f0SHans Verkuil struct vim2m_dev *dev; 1309dacca5f0SHans Verkuil struct video_device *vfd; 1310dacca5f0SHans Verkuil int ret; 1311dacca5f0SHans Verkuil 1312dacca5f0SHans Verkuil dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1313dacca5f0SHans Verkuil if (!dev) 1314dacca5f0SHans Verkuil return -ENOMEM; 1315dacca5f0SHans Verkuil 1316dacca5f0SHans Verkuil ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 1317dacca5f0SHans Verkuil if (ret) 1318dacca5f0SHans Verkuil goto error_free; 1319dacca5f0SHans Verkuil 1320dacca5f0SHans Verkuil atomic_set(&dev->num_inst, 0); 1321dacca5f0SHans Verkuil mutex_init(&dev->dev_mutex); 1322dacca5f0SHans Verkuil 1323dacca5f0SHans Verkuil dev->vfd = vim2m_videodev; 1324dacca5f0SHans Verkuil vfd = &dev->vfd; 1325dacca5f0SHans Verkuil vfd->lock = &dev->dev_mutex; 1326dacca5f0SHans Verkuil vfd->v4l2_dev = &dev->v4l2_dev; 1327dacca5f0SHans Verkuil 1328dacca5f0SHans Verkuil video_set_drvdata(vfd, dev); 1329dacca5f0SHans Verkuil v4l2_info(&dev->v4l2_dev, 1330dacca5f0SHans Verkuil "Device registered as /dev/video%d\n", vfd->num); 1331dacca5f0SHans Verkuil 1332dacca5f0SHans Verkuil platform_set_drvdata(pdev, dev); 1333dacca5f0SHans Verkuil 1334dacca5f0SHans Verkuil dev->m2m_dev = v4l2_m2m_init(&m2m_ops); 1335dacca5f0SHans Verkuil if (IS_ERR(dev->m2m_dev)) { 1336dacca5f0SHans Verkuil v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 1337dacca5f0SHans Verkuil ret = PTR_ERR(dev->m2m_dev); 1338dacca5f0SHans Verkuil dev->m2m_dev = NULL; 1339dacca5f0SHans Verkuil goto error_dev; 1340dacca5f0SHans Verkuil } 1341dacca5f0SHans Verkuil 1342*cf7f3477SSakari Ailus ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 1343*cf7f3477SSakari Ailus if (ret) { 1344*cf7f3477SSakari Ailus v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 1345*cf7f3477SSakari Ailus goto error_m2m; 1346*cf7f3477SSakari Ailus } 1347*cf7f3477SSakari Ailus 1348dacca5f0SHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 1349dacca5f0SHans Verkuil dev->mdev.dev = &pdev->dev; 1350dacca5f0SHans Verkuil strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); 1351dacca5f0SHans Verkuil strscpy(dev->mdev.bus_info, "platform:vim2m", 1352dacca5f0SHans Verkuil sizeof(dev->mdev.bus_info)); 1353dacca5f0SHans Verkuil media_device_init(&dev->mdev); 1354dacca5f0SHans Verkuil dev->mdev.ops = &m2m_media_ops; 1355dacca5f0SHans Verkuil dev->v4l2_dev.mdev = &dev->mdev; 1356dacca5f0SHans Verkuil 1357dacca5f0SHans Verkuil ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, 1358dacca5f0SHans Verkuil MEDIA_ENT_F_PROC_VIDEO_SCALER); 1359dacca5f0SHans Verkuil if (ret) { 1360dacca5f0SHans Verkuil v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); 1361*cf7f3477SSakari Ailus goto error_v4l2; 1362dacca5f0SHans Verkuil } 1363dacca5f0SHans Verkuil 1364dacca5f0SHans Verkuil ret = media_device_register(&dev->mdev); 1365dacca5f0SHans Verkuil if (ret) { 1366dacca5f0SHans Verkuil v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); 1367dacca5f0SHans Verkuil goto error_m2m_mc; 1368dacca5f0SHans Verkuil } 1369dacca5f0SHans Verkuil #endif 1370dacca5f0SHans Verkuil return 0; 1371dacca5f0SHans Verkuil 1372dacca5f0SHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 1373dacca5f0SHans Verkuil error_m2m_mc: 1374dacca5f0SHans Verkuil v4l2_m2m_unregister_media_controller(dev->m2m_dev); 1375dacca5f0SHans Verkuil #endif 1376*cf7f3477SSakari Ailus error_v4l2: 1377dacca5f0SHans Verkuil video_unregister_device(&dev->vfd); 1378dacca5f0SHans Verkuil /* vim2m_device_release called by video_unregister_device to release various objects */ 1379dacca5f0SHans Verkuil return ret; 1380*cf7f3477SSakari Ailus error_m2m: 1381*cf7f3477SSakari Ailus v4l2_m2m_release(dev->m2m_dev); 1382*cf7f3477SSakari Ailus error_dev: 1383dacca5f0SHans Verkuil v4l2_device_unregister(&dev->v4l2_dev); 1384dacca5f0SHans Verkuil error_free: 1385dacca5f0SHans Verkuil kfree(dev); 1386dacca5f0SHans Verkuil 1387dacca5f0SHans Verkuil return ret; 1388dacca5f0SHans Verkuil } 1389dacca5f0SHans Verkuil 1390dacca5f0SHans Verkuil static int vim2m_remove(struct platform_device *pdev) 1391dacca5f0SHans Verkuil { 1392dacca5f0SHans Verkuil struct vim2m_dev *dev = platform_get_drvdata(pdev); 1393dacca5f0SHans Verkuil 1394dacca5f0SHans Verkuil v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME); 1395dacca5f0SHans Verkuil 1396dacca5f0SHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 1397dacca5f0SHans Verkuil media_device_unregister(&dev->mdev); 1398dacca5f0SHans Verkuil v4l2_m2m_unregister_media_controller(dev->m2m_dev); 1399dacca5f0SHans Verkuil #endif 1400dacca5f0SHans Verkuil video_unregister_device(&dev->vfd); 1401dacca5f0SHans Verkuil 1402dacca5f0SHans Verkuil return 0; 1403dacca5f0SHans Verkuil } 1404dacca5f0SHans Verkuil 1405dacca5f0SHans Verkuil static struct platform_driver vim2m_pdrv = { 1406dacca5f0SHans Verkuil .probe = vim2m_probe, 1407dacca5f0SHans Verkuil .remove = vim2m_remove, 1408dacca5f0SHans Verkuil .driver = { 1409dacca5f0SHans Verkuil .name = MEM2MEM_NAME, 1410dacca5f0SHans Verkuil }, 1411dacca5f0SHans Verkuil }; 1412dacca5f0SHans Verkuil 1413dacca5f0SHans Verkuil static void __exit vim2m_exit(void) 1414dacca5f0SHans Verkuil { 1415dacca5f0SHans Verkuil platform_driver_unregister(&vim2m_pdrv); 1416dacca5f0SHans Verkuil platform_device_unregister(&vim2m_pdev); 1417dacca5f0SHans Verkuil } 1418dacca5f0SHans Verkuil 1419dacca5f0SHans Verkuil static int __init vim2m_init(void) 1420dacca5f0SHans Verkuil { 1421dacca5f0SHans Verkuil int ret; 1422dacca5f0SHans Verkuil 1423dacca5f0SHans Verkuil ret = platform_device_register(&vim2m_pdev); 1424dacca5f0SHans Verkuil if (ret) 1425dacca5f0SHans Verkuil return ret; 1426dacca5f0SHans Verkuil 1427dacca5f0SHans Verkuil ret = platform_driver_register(&vim2m_pdrv); 1428dacca5f0SHans Verkuil if (ret) 1429dacca5f0SHans Verkuil platform_device_unregister(&vim2m_pdev); 1430dacca5f0SHans Verkuil 1431dacca5f0SHans Verkuil return ret; 1432dacca5f0SHans Verkuil } 1433dacca5f0SHans Verkuil 1434dacca5f0SHans Verkuil module_init(vim2m_init); 1435dacca5f0SHans Verkuil module_exit(vim2m_exit); 1436