blob: 359f59efb44e33a975e4d824ddb040510daacea3 [file] [log] [blame]
Helen Koikef2fe8902017-04-07 14:55:19 -03001/*
2 * vimc-capture.c Virtual Media Controller Driver
3 *
4 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <media/v4l2-ioctl.h>
19#include <media/videobuf2-core.h>
20#include <media/videobuf2-vmalloc.h>
21
22#include "vimc-capture.h"
23
24struct vimc_cap_device {
25 struct vimc_ent_device ved;
26 struct video_device vdev;
27 struct v4l2_pix_format format;
28 struct vb2_queue queue;
29 struct list_head buf_list;
30 /*
31 * NOTE: in a real driver, a spin lock must be used to access the
32 * queue because the frames are generated from a hardware interruption
33 * and the isr is not allowed to sleep.
34 * Even if it is not necessary a spinlock in the vimc driver, we
35 * use it here as a code reference
36 */
37 spinlock_t qlock;
38 struct mutex lock;
39 u32 sequence;
40 struct media_pipeline pipe;
41};
42
Helen Fornazier535d2962017-06-19 14:00:17 -030043static const struct v4l2_pix_format fmt_default = {
44 .width = 640,
45 .height = 480,
46 .pixelformat = V4L2_PIX_FMT_RGB24,
47 .field = V4L2_FIELD_NONE,
48 .colorspace = V4L2_COLORSPACE_DEFAULT,
49};
50
Helen Koikef2fe8902017-04-07 14:55:19 -030051struct vimc_cap_buffer {
52 /*
53 * struct vb2_v4l2_buffer must be the first element
54 * the videobuf2 framework will allocate this struct based on
55 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
56 * memory as a vb2_buffer
57 */
58 struct vb2_v4l2_buffer vb2;
59 struct list_head list;
60};
61
62static int vimc_cap_querycap(struct file *file, void *priv,
63 struct v4l2_capability *cap)
64{
65 struct vimc_cap_device *vcap = video_drvdata(file);
66
67 strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
68 strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
69 snprintf(cap->bus_info, sizeof(cap->bus_info),
70 "platform:%s", vcap->vdev.v4l2_dev->name);
71
72 return 0;
73}
74
Helen Fornazier288a22d2017-06-19 14:00:14 -030075static void vimc_cap_get_format(struct vimc_ent_device *ved,
76 struct v4l2_pix_format *fmt)
77{
78 struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
79 ved);
80
81 *fmt = vcap->format;
82}
83
Helen Fornazier535d2962017-06-19 14:00:17 -030084static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
Helen Koikef2fe8902017-04-07 14:55:19 -030085 struct v4l2_format *f)
86{
87 struct vimc_cap_device *vcap = video_drvdata(file);
88
89 f->fmt.pix = vcap->format;
90
91 return 0;
92}
93
Helen Fornazier535d2962017-06-19 14:00:17 -030094static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
95 struct v4l2_format *f)
96{
97 struct v4l2_pix_format *format = &f->fmt.pix;
98 const struct vimc_pix_map *vpix;
99
100 format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH,
101 VIMC_FRAME_MAX_WIDTH) & ~1;
102 format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT,
103 VIMC_FRAME_MAX_HEIGHT) & ~1;
104
105 /* Don't accept a pixelformat that is not on the table */
106 vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
107 if (!vpix) {
108 format->pixelformat = fmt_default.pixelformat;
109 vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
110 }
111 /* TODO: Add support for custom bytesperline values */
112 format->bytesperline = format->width * vpix->bpp;
113 format->sizeimage = format->bytesperline * format->height;
114
115 if (format->field == V4L2_FIELD_ANY)
116 format->field = fmt_default.field;
117
118 vimc_colorimetry_clamp(format);
119
120 return 0;
121}
122
123static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
124 struct v4l2_format *f)
Helen Koikef2fe8902017-04-07 14:55:19 -0300125{
126 struct vimc_cap_device *vcap = video_drvdata(file);
127
Helen Fornazier535d2962017-06-19 14:00:17 -0300128 /* Do not change the format while stream is on */
129 if (vb2_is_busy(&vcap->queue))
130 return -EBUSY;
131
132 vimc_cap_try_fmt_vid_cap(file, priv, f);
133
134 dev_dbg(vcap->vdev.v4l2_dev->dev, "%s: format update: "
135 "old:%dx%d (0x%x, %d, %d, %d, %d) "
136 "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name,
137 /* old */
138 vcap->format.width, vcap->format.height,
139 vcap->format.pixelformat, vcap->format.colorspace,
140 vcap->format.quantization, vcap->format.xfer_func,
141 vcap->format.ycbcr_enc,
142 /* new */
143 f->fmt.pix.width, f->fmt.pix.height,
144 f->fmt.pix.pixelformat, f->fmt.pix.colorspace,
145 f->fmt.pix.quantization, f->fmt.pix.xfer_func,
146 f->fmt.pix.ycbcr_enc);
147
148 vcap->format = f->fmt.pix;
149
150 return 0;
151}
152
153static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
154 struct v4l2_fmtdesc *f)
155{
156 const struct vimc_pix_map *vpix = vimc_pix_map_by_index(f->index);
157
158 if (!vpix)
Helen Koikef2fe8902017-04-07 14:55:19 -0300159 return -EINVAL;
160
Helen Fornazier535d2962017-06-19 14:00:17 -0300161 f->pixelformat = vpix->pixelformat;
162
163 return 0;
164}
165
166static int vimc_cap_enum_framesizes(struct file *file, void *fh,
167 struct v4l2_frmsizeenum *fsize)
168{
169 const struct vimc_pix_map *vpix;
170
171 if (fsize->index)
172 return -EINVAL;
173
174 /* Only accept code in the pix map table */
175 vpix = vimc_pix_map_by_code(fsize->pixel_format);
176 if (!vpix)
177 return -EINVAL;
178
179 fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
180 fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH;
181 fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH;
182 fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT;
183 fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT;
184 fsize->stepwise.step_width = 2;
185 fsize->stepwise.step_height = 2;
Helen Koikef2fe8902017-04-07 14:55:19 -0300186
187 return 0;
188}
189
190static const struct v4l2_file_operations vimc_cap_fops = {
191 .owner = THIS_MODULE,
192 .open = v4l2_fh_open,
193 .release = vb2_fop_release,
194 .read = vb2_fop_read,
195 .poll = vb2_fop_poll,
196 .unlocked_ioctl = video_ioctl2,
197 .mmap = vb2_fop_mmap,
198};
199
200static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
201 .vidioc_querycap = vimc_cap_querycap,
202
Helen Fornazier535d2962017-06-19 14:00:17 -0300203 .vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
204 .vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
205 .vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
Helen Koikef2fe8902017-04-07 14:55:19 -0300206 .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
Helen Fornazier535d2962017-06-19 14:00:17 -0300207 .vidioc_enum_framesizes = vimc_cap_enum_framesizes,
Helen Koikef2fe8902017-04-07 14:55:19 -0300208
209 .vidioc_reqbufs = vb2_ioctl_reqbufs,
210 .vidioc_create_bufs = vb2_ioctl_create_bufs,
211 .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
212 .vidioc_querybuf = vb2_ioctl_querybuf,
213 .vidioc_qbuf = vb2_ioctl_qbuf,
214 .vidioc_dqbuf = vb2_ioctl_dqbuf,
215 .vidioc_expbuf = vb2_ioctl_expbuf,
216 .vidioc_streamon = vb2_ioctl_streamon,
217 .vidioc_streamoff = vb2_ioctl_streamoff,
218};
219
220static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
221 enum vb2_buffer_state state)
222{
223 struct vimc_cap_buffer *vbuf, *node;
224
225 spin_lock(&vcap->qlock);
226
227 list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
228 list_del(&vbuf->list);
229 vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
230 }
231
232 spin_unlock(&vcap->qlock);
233}
234
Helen Koikef2fe8902017-04-07 14:55:19 -0300235static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
236{
237 struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
238 struct media_entity *entity = &vcap->vdev.entity;
239 int ret;
240
241 vcap->sequence = 0;
242
243 /* Start the media pipeline */
244 ret = media_pipeline_start(entity, &vcap->pipe);
245 if (ret) {
246 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
247 return ret;
248 }
249
250 /* Enable streaming from the pipe */
Helen Fornazierbf5fb952017-06-19 14:00:13 -0300251 ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1);
Helen Koikef2fe8902017-04-07 14:55:19 -0300252 if (ret) {
253 media_pipeline_stop(entity);
254 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
255 return ret;
256 }
257
258 return 0;
259}
260
261/*
262 * Stop the stream engine. Any remaining buffers in the stream queue are
263 * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
264 */
265static void vimc_cap_stop_streaming(struct vb2_queue *vq)
266{
267 struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
268
269 /* Disable streaming from the pipe */
Helen Fornazierbf5fb952017-06-19 14:00:13 -0300270 vimc_pipeline_s_stream(&vcap->vdev.entity, 0);
Helen Koikef2fe8902017-04-07 14:55:19 -0300271
272 /* Stop the media pipeline */
273 media_pipeline_stop(&vcap->vdev.entity);
274
275 /* Release all active buffers */
276 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
277}
278
279static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
280{
281 struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
282 struct vimc_cap_buffer *buf = container_of(vb2_buf,
283 struct vimc_cap_buffer,
284 vb2.vb2_buf);
285
286 spin_lock(&vcap->qlock);
287 list_add_tail(&buf->list, &vcap->buf_list);
288 spin_unlock(&vcap->qlock);
289}
290
291static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
292 unsigned int *nplanes, unsigned int sizes[],
293 struct device *alloc_devs[])
294{
295 struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
296
297 if (*nplanes)
298 return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
299 /* We don't support multiplanes for now */
300 *nplanes = 1;
301 sizes[0] = vcap->format.sizeimage;
302
303 return 0;
304}
305
306static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
307{
308 struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
309 unsigned long size = vcap->format.sizeimage;
310
311 if (vb2_plane_size(vb, 0) < size) {
312 dev_err(vcap->vdev.v4l2_dev->dev,
313 "%s: buffer too small (%lu < %lu)\n",
314 vcap->vdev.name, vb2_plane_size(vb, 0), size);
315 return -EINVAL;
316 }
317 return 0;
318}
319
320static const struct vb2_ops vimc_cap_qops = {
321 .start_streaming = vimc_cap_start_streaming,
322 .stop_streaming = vimc_cap_stop_streaming,
323 .buf_queue = vimc_cap_buf_queue,
324 .queue_setup = vimc_cap_queue_setup,
325 .buf_prepare = vimc_cap_buffer_prepare,
326 /*
327 * Since q->lock is set we can use the standard
328 * vb2_ops_wait_prepare/finish helper functions.
329 */
330 .wait_prepare = vb2_ops_wait_prepare,
331 .wait_finish = vb2_ops_wait_finish,
332};
333
Helen Koikef2fe8902017-04-07 14:55:19 -0300334static const struct media_entity_operations vimc_cap_mops = {
Helen Fornazier288a22d2017-06-19 14:00:14 -0300335 .link_validate = vimc_link_validate,
Helen Koikef2fe8902017-04-07 14:55:19 -0300336};
337
338static void vimc_cap_destroy(struct vimc_ent_device *ved)
339{
340 struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
341 ved);
342
343 vb2_queue_release(&vcap->queue);
344 media_entity_cleanup(ved->ent);
345 video_unregister_device(&vcap->vdev);
346 vimc_pads_cleanup(vcap->ved.pads);
347 kfree(vcap);
348}
349
350static void vimc_cap_process_frame(struct vimc_ent_device *ved,
351 struct media_pad *sink, const void *frame)
352{
353 struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
354 ved);
355 struct vimc_cap_buffer *vimc_buf;
356 void *vbuf;
357
358 spin_lock(&vcap->qlock);
359
360 /* Get the first entry of the list */
361 vimc_buf = list_first_entry_or_null(&vcap->buf_list,
362 typeof(*vimc_buf), list);
363 if (!vimc_buf) {
364 spin_unlock(&vcap->qlock);
365 return;
366 }
367
368 /* Remove this entry from the list */
369 list_del(&vimc_buf->list);
370
371 spin_unlock(&vcap->qlock);
372
373 /* Fill the buffer */
374 vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
375 vimc_buf->vb2.sequence = vcap->sequence++;
376 vimc_buf->vb2.field = vcap->format.field;
377
378 vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
379
380 memcpy(vbuf, frame, vcap->format.sizeimage);
381
382 /* Set it as ready */
383 vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
384 vcap->format.sizeimage);
385 vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
386}
387
388struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
389 const char *const name,
390 u16 num_pads,
391 const unsigned long *pads_flag)
392{
393 const struct vimc_pix_map *vpix;
394 struct vimc_cap_device *vcap;
395 struct video_device *vdev;
396 struct vb2_queue *q;
397 int ret;
398
399 /*
400 * Check entity configuration params
401 * NOTE: we only support a single sink pad
402 */
403 if (!name || num_pads != 1 || !pads_flag ||
404 !(pads_flag[0] & MEDIA_PAD_FL_SINK))
405 return ERR_PTR(-EINVAL);
406
407 /* Allocate the vimc_cap_device struct */
408 vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
409 if (!vcap)
410 return ERR_PTR(-ENOMEM);
411
412 /* Allocate the pads */
413 vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
414 if (IS_ERR(vcap->ved.pads)) {
415 ret = PTR_ERR(vcap->ved.pads);
416 goto err_free_vcap;
417 }
418
419 /* Initialize the media entity */
420 vcap->vdev.entity.name = name;
421 vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
422 ret = media_entity_pads_init(&vcap->vdev.entity,
423 num_pads, vcap->ved.pads);
424 if (ret)
425 goto err_clean_pads;
426
427 /* Initialize the lock */
428 mutex_init(&vcap->lock);
429
430 /* Initialize the vb2 queue */
431 q = &vcap->queue;
432 q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
433 q->io_modes = VB2_MMAP | VB2_DMABUF;
434 q->drv_priv = vcap;
435 q->buf_struct_size = sizeof(struct vimc_cap_buffer);
436 q->ops = &vimc_cap_qops;
437 q->mem_ops = &vb2_vmalloc_memops;
438 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
439 q->min_buffers_needed = 2;
440 q->lock = &vcap->lock;
441
442 ret = vb2_queue_init(q);
443 if (ret) {
444 dev_err(vcap->vdev.v4l2_dev->dev,
445 "%s: vb2 queue init failed (err=%d)\n",
446 vcap->vdev.name, ret);
447 goto err_clean_m_ent;
448 }
449
450 /* Initialize buffer list and its lock */
451 INIT_LIST_HEAD(&vcap->buf_list);
452 spin_lock_init(&vcap->qlock);
453
Helen Fornazier535d2962017-06-19 14:00:17 -0300454 /* Set default frame format */
455 vcap->format = fmt_default;
Helen Koikef2fe8902017-04-07 14:55:19 -0300456 vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
Helen Koikef2fe8902017-04-07 14:55:19 -0300457 vcap->format.bytesperline = vcap->format.width * vpix->bpp;
458 vcap->format.sizeimage = vcap->format.bytesperline *
459 vcap->format.height;
460
461 /* Fill the vimc_ent_device struct */
462 vcap->ved.destroy = vimc_cap_destroy;
463 vcap->ved.ent = &vcap->vdev.entity;
464 vcap->ved.process_frame = vimc_cap_process_frame;
Helen Fornazier288a22d2017-06-19 14:00:14 -0300465 vcap->ved.vdev_get_format = vimc_cap_get_format;
Helen Koikef2fe8902017-04-07 14:55:19 -0300466
467 /* Initialize the video_device struct */
468 vdev = &vcap->vdev;
469 vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
470 vdev->entity.ops = &vimc_cap_mops;
471 vdev->release = video_device_release_empty;
472 vdev->fops = &vimc_cap_fops;
473 vdev->ioctl_ops = &vimc_cap_ioctl_ops;
474 vdev->lock = &vcap->lock;
475 vdev->queue = q;
476 vdev->v4l2_dev = v4l2_dev;
477 vdev->vfl_dir = VFL_DIR_RX;
478 strlcpy(vdev->name, name, sizeof(vdev->name));
479 video_set_drvdata(vdev, &vcap->ved);
480
481 /* Register the video_device with the v4l2 and the media framework */
482 ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
483 if (ret) {
484 dev_err(vcap->vdev.v4l2_dev->dev,
485 "%s: video register failed (err=%d)\n",
486 vcap->vdev.name, ret);
487 goto err_release_queue;
488 }
489
490 return &vcap->ved;
491
492err_release_queue:
493 vb2_queue_release(q);
494err_clean_m_ent:
495 media_entity_cleanup(&vcap->vdev.entity);
496err_clean_pads:
497 vimc_pads_cleanup(vcap->ved.pads);
498err_free_vcap:
499 kfree(vcap);
500
501 return ERR_PTR(ret);
502}