1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform
4 *
5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which
6 * used to process image from camera sensor or memory to memory or DC
7 *
8 * Copyright (c) 2019 NXP Semiconductor
9 */
10
11 #include <linux/container_of.h>
12 #include <linux/device.h>
13 #include <linux/errno.h>
14 #include <linux/kernel.h>
15 #include <linux/limits.h>
16 #include <linux/minmax.h>
17 #include <linux/mutex.h>
18 #include <linux/pm_runtime.h>
19 #include <linux/slab.h>
20 #include <linux/spinlock.h>
21 #include <linux/string.h>
22 #include <linux/types.h>
23 #include <linux/videodev2.h>
24
25 #include <media/media-entity.h>
26 #include <media/v4l2-ctrls.h>
27 #include <media/v4l2-device.h>
28 #include <media/v4l2-event.h>
29 #include <media/v4l2-fh.h>
30 #include <media/v4l2-ioctl.h>
31 #include <media/v4l2-mem2mem.h>
32 #include <media/videobuf2-core.h>
33 #include <media/videobuf2-dma-contig.h>
34
35 #include "imx8-isi-core.h"
36
37 struct mxc_isi_m2m_buffer {
38 struct v4l2_m2m_buffer buf;
39 dma_addr_t dma_addrs[3];
40 };
41
42 struct mxc_isi_m2m_ctx_queue_data {
43 struct v4l2_pix_format_mplane format;
44 const struct mxc_isi_format_info *info;
45 u32 sequence;
46 };
47
48 struct mxc_isi_m2m_ctx {
49 struct v4l2_fh fh;
50 struct mxc_isi_m2m *m2m;
51
52 /* Protects the m2m vb2 queues */
53 struct mutex vb2_lock;
54
55 struct {
56 struct mxc_isi_m2m_ctx_queue_data out;
57 struct mxc_isi_m2m_ctx_queue_data cap;
58 } queues;
59
60 struct {
61 struct v4l2_ctrl_handler handler;
62 unsigned int alpha;
63 bool hflip;
64 bool vflip;
65 } ctrls;
66
67 bool chained;
68 };
69
70 static inline struct mxc_isi_m2m_buffer *
to_isi_m2m_buffer(struct vb2_v4l2_buffer * buf)71 to_isi_m2m_buffer(struct vb2_v4l2_buffer *buf)
72 {
73 return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb);
74 }
75
file_to_isi_m2m_ctx(struct file * filp)76 static inline struct mxc_isi_m2m_ctx *file_to_isi_m2m_ctx(struct file *filp)
77 {
78 return container_of(file_to_v4l2_fh(filp), struct mxc_isi_m2m_ctx, fh);
79 }
80
81 static inline struct mxc_isi_m2m_ctx_queue_data *
mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx * ctx,enum v4l2_buf_type type)82 mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx *ctx, enum v4l2_buf_type type)
83 {
84 if (V4L2_TYPE_IS_OUTPUT(type))
85 return &ctx->queues.out;
86 else
87 return &ctx->queues.cap;
88 }
89
90 /* -----------------------------------------------------------------------------
91 * V4L2 M2M device operations
92 */
93
mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe * pipe,u32 status)94 static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status)
95 {
96 struct mxc_isi_m2m *m2m = &pipe->isi->m2m;
97 struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
98 struct mxc_isi_m2m_ctx *ctx;
99
100 ctx = v4l2_m2m_get_curr_priv(m2m->m2m_dev);
101 if (!ctx) {
102 dev_err(m2m->isi->dev,
103 "Instance released before the end of transaction\n");
104 return;
105 }
106
107 src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
108 dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
109
110 v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf);
111
112 src_vbuf->sequence = ctx->queues.out.sequence++;
113 dst_vbuf->sequence = ctx->queues.cap.sequence++;
114
115 v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_DONE);
116 v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_DONE);
117
118 v4l2_m2m_job_finish(m2m->m2m_dev, ctx->fh.m2m_ctx);
119 }
120
mxc_isi_m2m_device_run(void * priv)121 static void mxc_isi_m2m_device_run(void *priv)
122 {
123 struct mxc_isi_m2m_ctx *ctx = priv;
124 struct mxc_isi_m2m *m2m = ctx->m2m;
125 struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
126 struct mxc_isi_m2m_buffer *src_buf, *dst_buf;
127
128 mxc_isi_channel_disable(m2m->pipe);
129
130 mutex_lock(&m2m->lock);
131
132 /* If the context has changed, reconfigure the channel. */
133 if (m2m->last_ctx != ctx) {
134 const struct v4l2_area in_size = {
135 .width = ctx->queues.out.format.width,
136 .height = ctx->queues.out.format.height,
137 };
138 const struct v4l2_area scale = {
139 .width = ctx->queues.cap.format.width,
140 .height = ctx->queues.cap.format.height,
141 };
142 const struct v4l2_rect crop = {
143 .width = ctx->queues.cap.format.width,
144 .height = ctx->queues.cap.format.height,
145 };
146
147 mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM,
148 &in_size, &scale, &crop,
149 ctx->queues.out.info->encoding,
150 ctx->queues.cap.info->encoding);
151 mxc_isi_channel_set_input_format(m2m->pipe,
152 ctx->queues.out.info,
153 &ctx->queues.out.format);
154 mxc_isi_channel_set_output_format(m2m->pipe,
155 ctx->queues.cap.info,
156 &ctx->queues.cap.format);
157
158 m2m->last_ctx = ctx;
159 }
160
161 mutex_unlock(&m2m->lock);
162
163 mutex_lock(ctx->ctrls.handler.lock);
164 mxc_isi_channel_set_alpha(m2m->pipe, ctx->ctrls.alpha);
165 mxc_isi_channel_set_flip(m2m->pipe, ctx->ctrls.hflip, ctx->ctrls.vflip);
166 mutex_unlock(ctx->ctrls.handler.lock);
167
168 src_vbuf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
169 dst_vbuf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
170
171 src_buf = to_isi_m2m_buffer(src_vbuf);
172 dst_buf = to_isi_m2m_buffer(dst_vbuf);
173
174 mxc_isi_channel_set_inbuf(m2m->pipe, src_buf->dma_addrs[0]);
175 mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF1);
176 mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF2);
177
178 mxc_isi_channel_enable(m2m->pipe);
179
180 mxc_isi_channel_m2m_start(m2m->pipe);
181 }
182
183 static const struct v4l2_m2m_ops mxc_isi_m2m_ops = {
184 .device_run = mxc_isi_m2m_device_run,
185 };
186
187 /* -----------------------------------------------------------------------------
188 * videobuf2 queue operations
189 */
190
mxc_isi_m2m_vb2_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])191 static int mxc_isi_m2m_vb2_queue_setup(struct vb2_queue *q,
192 unsigned int *num_buffers,
193 unsigned int *num_planes,
194 unsigned int sizes[],
195 struct device *alloc_devs[])
196 {
197 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
198 const struct mxc_isi_m2m_ctx_queue_data *qdata =
199 mxc_isi_m2m_ctx_qdata(ctx, q->type);
200
201 return mxc_isi_video_queue_setup(&qdata->format, qdata->info,
202 num_buffers, num_planes, sizes);
203 }
204
mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer * vb2)205 static int mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer *vb2)
206 {
207 struct vb2_queue *vq = vb2->vb2_queue;
208 struct mxc_isi_m2m_buffer *buf = to_isi_m2m_buffer(to_vb2_v4l2_buffer(vb2));
209 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue);
210 const struct mxc_isi_m2m_ctx_queue_data *qdata =
211 mxc_isi_m2m_ctx_qdata(ctx, vq->type);
212
213 mxc_isi_video_buffer_init(vb2, buf->dma_addrs, qdata->info,
214 &qdata->format);
215
216 return 0;
217 }
218
mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer * vb2)219 static int mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer *vb2)
220 {
221 struct vb2_queue *vq = vb2->vb2_queue;
222 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vq);
223 const struct mxc_isi_m2m_ctx_queue_data *qdata =
224 mxc_isi_m2m_ctx_qdata(ctx, vq->type);
225
226 return mxc_isi_video_buffer_prepare(ctx->m2m->isi, vb2, qdata->info,
227 &qdata->format);
228 }
229
mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer * vb2)230 static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2)
231 {
232 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
233 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue);
234
235 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
236 }
237
mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue * q)238 static int mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue *q)
239 {
240 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
241 const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
242 const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
243 const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
244 const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
245 struct mxc_isi_m2m *m2m = ctx->m2m;
246 int ret;
247
248 guard(mutex)(&m2m->lock);
249
250 if (m2m->usage_count == INT_MAX)
251 return -EOVERFLOW;
252
253 ret = pm_runtime_resume_and_get(m2m->isi->dev);
254 if (ret)
255 return ret;
256
257 /*
258 * Acquire the pipe and initialize the channel with the first user of
259 * the M2M device.
260 */
261 if (m2m->usage_count == 0) {
262 bool bypass = cap_pix->width == out_pix->width &&
263 cap_pix->height == out_pix->height &&
264 cap_info->encoding == out_info->encoding;
265
266 ret = mxc_isi_channel_acquire(m2m->pipe,
267 &mxc_isi_m2m_frame_write_done,
268 bypass);
269 if (ret)
270 goto err_pm;
271
272 mxc_isi_channel_get(m2m->pipe);
273 }
274
275 m2m->usage_count++;
276
277 /*
278 * Allocate resources for the channel, counting how many users require
279 * buffer chaining.
280 */
281 if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
282 ret = mxc_isi_channel_chain(m2m->pipe);
283 if (ret)
284 goto err_deinit;
285
286 m2m->chained_count++;
287 ctx->chained = true;
288 }
289
290 return 0;
291
292 err_deinit:
293 if (--m2m->usage_count == 0) {
294 mxc_isi_channel_put(m2m->pipe);
295 mxc_isi_channel_release(m2m->pipe);
296 }
297 err_pm:
298 pm_runtime_put(m2m->isi->dev);
299 return ret;
300 }
301
mxc_isi_m2m_vb2_start_streaming(struct vb2_queue * q,unsigned int count)302 static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q,
303 unsigned int count)
304 {
305 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
306 struct mxc_isi_m2m_ctx_queue_data *qdata =
307 mxc_isi_m2m_ctx_qdata(ctx, q->type);
308
309 qdata->sequence = 0;
310
311 return 0;
312 }
313
mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue * q)314 static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q)
315 {
316 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
317 struct vb2_v4l2_buffer *vbuf;
318
319 for (;;) {
320 if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
321 vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
322 else
323 vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
324 if (!vbuf)
325 break;
326
327 v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
328 }
329 }
330
mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue * q)331 static void mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue *q)
332 {
333 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
334 struct mxc_isi_m2m *m2m = ctx->m2m;
335
336 guard(mutex)(&m2m->lock);
337
338 /*
339 * If the last context is this one, reset it to make sure the device
340 * will be reconfigured when streaming is restarted.
341 */
342 if (m2m->last_ctx == ctx)
343 m2m->last_ctx = NULL;
344
345 /* Free the channel resources if this is the last chained context. */
346 if (ctx->chained && --m2m->chained_count == 0)
347 mxc_isi_channel_unchain(m2m->pipe);
348 ctx->chained = false;
349
350 /* Turn off the light with the last user. */
351 if (--m2m->usage_count == 0) {
352 mxc_isi_channel_disable(m2m->pipe);
353 mxc_isi_channel_put(m2m->pipe);
354 mxc_isi_channel_release(m2m->pipe);
355 }
356
357 WARN_ON(m2m->usage_count < 0);
358
359 pm_runtime_put(m2m->isi->dev);
360 }
361
362 static const struct vb2_ops mxc_isi_m2m_vb2_qops = {
363 .queue_setup = mxc_isi_m2m_vb2_queue_setup,
364 .buf_init = mxc_isi_m2m_vb2_buffer_init,
365 .buf_prepare = mxc_isi_m2m_vb2_buffer_prepare,
366 .buf_queue = mxc_isi_m2m_vb2_buffer_queue,
367 .prepare_streaming = mxc_isi_m2m_vb2_prepare_streaming,
368 .start_streaming = mxc_isi_m2m_vb2_start_streaming,
369 .stop_streaming = mxc_isi_m2m_vb2_stop_streaming,
370 .unprepare_streaming = mxc_isi_m2m_vb2_unprepare_streaming,
371 };
372
mxc_isi_m2m_queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)373 static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
374 struct vb2_queue *dst_vq)
375 {
376 struct mxc_isi_m2m_ctx *ctx = priv;
377 struct mxc_isi_m2m *m2m = ctx->m2m;
378 int ret;
379
380 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
381 src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
382 src_vq->drv_priv = ctx;
383 src_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);
384 src_vq->ops = &mxc_isi_m2m_vb2_qops;
385 src_vq->mem_ops = &vb2_dma_contig_memops;
386 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
387 src_vq->lock = &ctx->vb2_lock;
388 src_vq->dev = m2m->isi->dev;
389
390 ret = vb2_queue_init(src_vq);
391 if (ret)
392 return ret;
393
394 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
395 dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
396 dst_vq->drv_priv = ctx;
397 dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);
398 dst_vq->ops = &mxc_isi_m2m_vb2_qops;
399 dst_vq->mem_ops = &vb2_dma_contig_memops;
400 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
401 dst_vq->lock = &ctx->vb2_lock;
402 dst_vq->dev = m2m->isi->dev;
403
404 return vb2_queue_init(dst_vq);
405 }
406
407 /* -----------------------------------------------------------------------------
408 * V4L2 controls
409 */
410
411 static inline struct mxc_isi_m2m_ctx *
ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl * ctrl)412 ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl)
413 {
414 return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler);
415 }
416
mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl * ctrl)417 static int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl)
418 {
419 struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl);
420
421 switch (ctrl->id) {
422 case V4L2_CID_HFLIP:
423 ctx->ctrls.hflip = ctrl->val;
424 break;
425
426 case V4L2_CID_VFLIP:
427 ctx->ctrls.vflip = ctrl->val;
428 break;
429
430 case V4L2_CID_ALPHA_COMPONENT:
431 ctx->ctrls.alpha = ctrl->val;
432 break;
433 }
434
435 return 0;
436 }
437
438 static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = {
439 .s_ctrl = mxc_isi_m2m_ctx_s_ctrl,
440 };
441
mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx * ctx)442 static int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx)
443 {
444 struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler;
445 int ret;
446
447 v4l2_ctrl_handler_init(handler, 3);
448
449 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
450 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
451 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
452 V4L2_CID_HFLIP, 0, 1, 1, 0);
453 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
454 V4L2_CID_VFLIP, 0, 1, 1, 0);
455
456 if (handler->error) {
457 ret = handler->error;
458 v4l2_ctrl_handler_free(handler);
459 return ret;
460 }
461
462 ctx->fh.ctrl_handler = handler;
463
464 return 0;
465 }
466
mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx * ctx)467 static void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx)
468 {
469 v4l2_ctrl_handler_free(&ctx->ctrls.handler);
470 }
471
472 /* -----------------------------------------------------------------------------
473 * V4L2 ioctls
474 */
475
mxc_isi_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)476 static int mxc_isi_m2m_querycap(struct file *file, void *fh,
477 struct v4l2_capability *cap)
478 {
479 strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));
480 strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card));
481 cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
482 cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
483
484 return 0;
485 }
486
mxc_isi_m2m_enum_fmt_vid(struct file * file,void * fh,struct v4l2_fmtdesc * f)487 static int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh,
488 struct v4l2_fmtdesc *f)
489 {
490 const enum mxc_isi_video_type type =
491 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
492 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
493 const struct mxc_isi_format_info *info;
494
495 info = mxc_isi_format_enum(f->index, type);
496 if (!info)
497 return -EINVAL;
498
499 f->pixelformat = info->fourcc;
500 f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC
501 | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;
502
503 return 0;
504 }
505
506 static const struct mxc_isi_format_info *
__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx * ctx,struct v4l2_pix_format_mplane * pix,const enum mxc_isi_video_type type)507 __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx,
508 struct v4l2_pix_format_mplane *pix,
509 const enum mxc_isi_video_type type)
510 {
511 if (type == MXC_ISI_VIDEO_M2M_CAP) {
512 /* Downscaling only */
513 pix->width = min(pix->width, ctx->queues.out.format.width);
514 pix->height = min(pix->height, ctx->queues.out.format.height);
515 }
516
517 return mxc_isi_format_try(ctx->m2m->pipe, pix, type);
518 }
519
mxc_isi_m2m_try_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)520 static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh,
521 struct v4l2_format *f)
522 {
523 const enum mxc_isi_video_type type =
524 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
525 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
526 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file);
527
528 __mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type);
529
530 return 0;
531 }
532
mxc_isi_m2m_g_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)533 static int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh,
534 struct v4l2_format *f)
535 {
536 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file);
537 const struct mxc_isi_m2m_ctx_queue_data *qdata =
538 mxc_isi_m2m_ctx_qdata(ctx, f->type);
539
540 f->fmt.pix_mp = qdata->format;
541
542 return 0;
543 }
544
mxc_isi_m2m_s_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)545 static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh,
546 struct v4l2_format *f)
547 {
548 const enum mxc_isi_video_type type =
549 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
550 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
551 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file);
552 struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
553 const struct mxc_isi_format_info *info;
554 struct vb2_queue *vq;
555
556 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
557
558 if (vb2_is_busy(vq))
559 return -EBUSY;
560
561 info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type);
562
563 if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
564 ctx->queues.out.format = *pix;
565 ctx->queues.out.info = info;
566 }
567
568 /*
569 * Always set the format on the capture side, due to either format
570 * propagation or direct setting.
571 */
572 ctx->queues.cap.format = *pix;
573 ctx->queues.cap.info = info;
574
575 return 0;
576 }
577
578 static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = {
579 .vidioc_querycap = mxc_isi_m2m_querycap,
580
581 .vidioc_enum_fmt_vid_cap = mxc_isi_m2m_enum_fmt_vid,
582 .vidioc_enum_fmt_vid_out = mxc_isi_m2m_enum_fmt_vid,
583 .vidioc_g_fmt_vid_cap_mplane = mxc_isi_m2m_g_fmt_vid,
584 .vidioc_g_fmt_vid_out_mplane = mxc_isi_m2m_g_fmt_vid,
585 .vidioc_s_fmt_vid_cap_mplane = mxc_isi_m2m_s_fmt_vid,
586 .vidioc_s_fmt_vid_out_mplane = mxc_isi_m2m_s_fmt_vid,
587 .vidioc_try_fmt_vid_cap_mplane = mxc_isi_m2m_try_fmt_vid,
588 .vidioc_try_fmt_vid_out_mplane = mxc_isi_m2m_try_fmt_vid,
589
590 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
591 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
592 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
593 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
594 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
595 .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
596 .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
597
598 .vidioc_streamon = v4l2_m2m_ioctl_streamon,
599 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
600
601 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
602 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
603 };
604
605 /* -----------------------------------------------------------------------------
606 * Video device file operations
607 */
608
mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx * ctx,struct mxc_isi_m2m_ctx_queue_data * qdata,enum mxc_isi_video_type type)609 static void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx,
610 struct mxc_isi_m2m_ctx_queue_data *qdata,
611 enum mxc_isi_video_type type)
612 {
613 qdata->format.width = MXC_ISI_DEF_WIDTH;
614 qdata->format.height = MXC_ISI_DEF_HEIGHT;
615 qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT;
616
617 qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type);
618 }
619
mxc_isi_m2m_open(struct file * file)620 static int mxc_isi_m2m_open(struct file *file)
621 {
622 struct video_device *vdev = video_devdata(file);
623 struct mxc_isi_m2m *m2m = video_drvdata(file);
624 struct mxc_isi_m2m_ctx *ctx;
625 int ret;
626
627 ctx = kzalloc_obj(*ctx);
628 if (!ctx)
629 return -ENOMEM;
630
631 ctx->m2m = m2m;
632 mutex_init(&ctx->vb2_lock);
633
634 v4l2_fh_init(&ctx->fh, vdev);
635
636 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx,
637 &mxc_isi_m2m_queue_init);
638 if (IS_ERR(ctx->fh.m2m_ctx)) {
639 ret = PTR_ERR(ctx->fh.m2m_ctx);
640 ctx->fh.m2m_ctx = NULL;
641 goto err_fh;
642 }
643
644 mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT);
645 mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP);
646
647 ret = mxc_isi_m2m_ctx_ctrls_create(ctx);
648 if (ret)
649 goto err_ctx;
650
651 v4l2_fh_add(&ctx->fh, file);
652
653 return 0;
654
655 err_ctx:
656 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
657 err_fh:
658 v4l2_fh_exit(&ctx->fh);
659 mutex_destroy(&ctx->vb2_lock);
660 kfree(ctx);
661 return ret;
662 }
663
mxc_isi_m2m_release(struct file * file)664 static int mxc_isi_m2m_release(struct file *file)
665 {
666 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file);
667
668 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
669 mxc_isi_m2m_ctx_ctrls_delete(ctx);
670
671 v4l2_fh_del(&ctx->fh, file);
672 v4l2_fh_exit(&ctx->fh);
673
674 mutex_destroy(&ctx->vb2_lock);
675 kfree(ctx);
676
677 return 0;
678 }
679
680 static const struct v4l2_file_operations mxc_isi_m2m_fops = {
681 .owner = THIS_MODULE,
682 .open = mxc_isi_m2m_open,
683 .release = mxc_isi_m2m_release,
684 .poll = v4l2_m2m_fop_poll,
685 .unlocked_ioctl = video_ioctl2,
686 .mmap = v4l2_m2m_fop_mmap,
687 };
688
689 /* -----------------------------------------------------------------------------
690 * Suspend & resume
691 */
692
mxc_isi_m2m_suspend(struct mxc_isi_m2m * m2m)693 void mxc_isi_m2m_suspend(struct mxc_isi_m2m *m2m)
694 {
695 if (m2m->usage_count == 0)
696 return;
697
698 v4l2_m2m_suspend(m2m->m2m_dev);
699
700 if (m2m->chained_count > 0)
701 mxc_isi_channel_unchain(m2m->pipe);
702
703 mxc_isi_channel_disable(m2m->pipe);
704 mxc_isi_channel_put(m2m->pipe);
705 }
706
mxc_isi_m2m_resume(struct mxc_isi_m2m * m2m)707 int mxc_isi_m2m_resume(struct mxc_isi_m2m *m2m)
708 {
709 if (m2m->usage_count == 0)
710 return 0;
711
712 mxc_isi_channel_get(m2m->pipe);
713
714 if (m2m->chained_count > 0)
715 mxc_isi_channel_chain(m2m->pipe);
716
717 m2m->last_ctx = NULL;
718 v4l2_m2m_resume(m2m->m2m_dev);
719
720 return 0;
721 }
722
723 /* -----------------------------------------------------------------------------
724 * Registration
725 */
726
mxc_isi_m2m_register(struct mxc_isi_dev * isi,struct v4l2_device * v4l2_dev)727 int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev)
728 {
729 struct mxc_isi_m2m *m2m = &isi->m2m;
730 struct video_device *vdev = &m2m->vdev;
731 struct media_link *link;
732 int ret;
733
734 m2m->isi = isi;
735 m2m->pipe = &isi->pipes[0];
736
737 mutex_init(&m2m->lock);
738
739 /* Initialize the video device and create controls. */
740 snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m");
741
742 vdev->fops = &mxc_isi_m2m_fops;
743 vdev->ioctl_ops = &mxc_isi_m2m_ioctl_ops;
744 vdev->v4l2_dev = v4l2_dev;
745 vdev->minor = -1;
746 vdev->release = video_device_release_empty;
747 vdev->vfl_dir = VFL_DIR_M2M;
748
749 vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
750 video_set_drvdata(vdev, m2m);
751
752 /* Create the M2M device. */
753 m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops);
754 if (IS_ERR(m2m->m2m_dev)) {
755 dev_err(isi->dev, "failed to initialize m2m device\n");
756 ret = PTR_ERR(m2m->m2m_dev);
757 goto err_mutex;
758 }
759
760 /* Register the video device. */
761 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
762 if (ret < 0) {
763 dev_err(isi->dev, "failed to register m2m device\n");
764 goto err_m2m;
765 }
766
767 /*
768 * Populate the media graph. We can't use the mem2mem helper
769 * v4l2_m2m_register_media_controller() as the M2M interface needs to
770 * be connected to the existing entities in the graph, so we have to
771 * wire things up manually:
772 *
773 * - The entity in the video_device, which isn't touched by the V4L2
774 * core for M2M devices, is used as the source I/O entity in the
775 * graph, connected to the crossbar switch.
776 *
777 * - The video device at the end of the pipeline provides the sink
778 * entity, and is already wired up in the graph.
779 *
780 * - A new interface is created, pointing at both entities. The sink
781 * entity will thus have two interfaces pointing to it.
782 */
783 m2m->pad.flags = MEDIA_PAD_FL_SOURCE;
784 vdev->entity.name = "mxc_isi.output";
785 vdev->entity.function = MEDIA_ENT_F_IO_V4L;
786 ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad);
787 if (ret)
788 goto err_video;
789
790 ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity);
791 if (ret)
792 goto err_entity_cleanup;
793
794 ret = media_create_pad_link(&vdev->entity, 0,
795 &m2m->isi->crossbar.sd.entity,
796 m2m->isi->crossbar.num_sinks - 1,
797 MEDIA_LNK_FL_IMMUTABLE |
798 MEDIA_LNK_FL_ENABLED);
799 if (ret)
800 goto err_entity_unreg;
801
802 m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO,
803 0, VIDEO_MAJOR, vdev->minor);
804 if (!m2m->intf) {
805 ret = -ENOMEM;
806 goto err_entity_unreg;
807 }
808
809 link = media_create_intf_link(&vdev->entity, &m2m->intf->intf,
810 MEDIA_LNK_FL_IMMUTABLE |
811 MEDIA_LNK_FL_ENABLED);
812 if (!link) {
813 ret = -ENOMEM;
814 goto err_devnode;
815 }
816
817 link = media_create_intf_link(&m2m->pipe->video.vdev.entity,
818 &m2m->intf->intf,
819 MEDIA_LNK_FL_IMMUTABLE |
820 MEDIA_LNK_FL_ENABLED);
821 if (!link) {
822 ret = -ENOMEM;
823 goto err_devnode;
824 }
825
826 return 0;
827
828 err_devnode:
829 media_devnode_remove(m2m->intf);
830 err_entity_unreg:
831 media_device_unregister_entity(&vdev->entity);
832 err_entity_cleanup:
833 media_entity_cleanup(&vdev->entity);
834 err_video:
835 video_unregister_device(vdev);
836 err_m2m:
837 v4l2_m2m_release(m2m->m2m_dev);
838 err_mutex:
839 mutex_destroy(&m2m->lock);
840 return ret;
841 }
842
mxc_isi_m2m_unregister(struct mxc_isi_dev * isi)843 int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi)
844 {
845 struct mxc_isi_m2m *m2m = &isi->m2m;
846 struct video_device *vdev = &m2m->vdev;
847
848 video_unregister_device(vdev);
849
850 v4l2_m2m_release(m2m->m2m_dev);
851 media_devnode_remove(m2m->intf);
852 media_entity_cleanup(&vdev->entity);
853 mutex_destroy(&m2m->lock);
854
855 return 0;
856 }
857