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