File Index
Files and directories
Sign in
FILE

imx8-isi-m2m.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c C
  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
 37struct mxc_isi_m2m_buffer {
 38	struct v4l2_m2m_buffer buf;
 39	dma_addr_t dma_addrs[3];
 40};
 41
 42struct 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
 48struct 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
 70static inline struct mxc_isi_m2m_buffer *
 71to_isi_m2m_buffer(struct vb2_v4l2_buffer *buf)
 72{
 73	return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb);
 74}
 75
 76static 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
 81static inline struct mxc_isi_m2m_ctx_queue_data *
 82mxc_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
 94static 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
121static 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
183static 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
191static 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
205static 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
219static 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
230static 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
238static 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
292err_deinit:
293	if (--m2m->usage_count == 0) {
294		mxc_isi_channel_put(m2m->pipe);
295		mxc_isi_channel_release(m2m->pipe);
296	}
297err_pm:
298	pm_runtime_put(m2m->isi->dev);
299	return ret;
300}
301
302static 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
314static 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
331static 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
362static 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
373static 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
411static inline struct mxc_isi_m2m_ctx *
412ctrl_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
417static 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
438static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = {
439	.s_ctrl = mxc_isi_m2m_ctx_s_ctrl,
440};
441
442static 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
467static 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
476static 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
487static 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
506static const struct mxc_isi_format_info *
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
520static 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
533static 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
545static 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
578static 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
609static 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
620static 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(sizeof(*ctx), GFP_KERNEL);
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
655err_ctx:
656	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
657err_fh:
658	v4l2_fh_exit(&ctx->fh);
659	mutex_destroy(&ctx->vb2_lock);
660	kfree(ctx);
661	return ret;
662}
663
664static 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
680static 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
693void 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
707int 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
727int 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
828err_devnode:
829	media_devnode_remove(m2m->intf);
830err_entity_unreg:
831	media_device_unregister_entity(&vdev->entity);
832err_entity_cleanup:
833	media_entity_cleanup(&vdev->entity);
834err_video:
835	video_unregister_device(vdev);
836err_m2m:
837	v4l2_m2m_release(m2m->m2m_dev);
838err_mutex:
839	mutex_destroy(&m2m->lock);
840	return ret;
841}
842
843int 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}
~/ Read-only file preview.