brintos

brintos / linux-shallow public Read only

0
0
Text · 22.3 KiB · 9745d62 Raw
859 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform4 *5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which6 * used to process image from camera sensor or memory to memory or DC7 *8 * Copyright (c) 2019 NXP Semiconductor9 */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 *to_isi_m2m_ctx(struct v4l2_fh *fh)77{78	return container_of(fh, 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	else87		return &ctx->queues.cap;88}89 90/* -----------------------------------------------------------------------------91 * V4L2 M2M device operations92 */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, false);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 operations189 */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_start_streaming(struct vb2_queue *q,239					   unsigned int count)240{241	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);242	struct mxc_isi_m2m_ctx_queue_data *qdata =243		mxc_isi_m2m_ctx_qdata(ctx, q->type);244 245	qdata->sequence = 0;246 247	return 0;248}249 250static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q)251{252	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);253	struct vb2_v4l2_buffer *vbuf;254 255	for (;;) {256		if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)257			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);258		else259			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);260		if (!vbuf)261			break;262 263		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);264	}265}266 267static const struct vb2_ops mxc_isi_m2m_vb2_qops = {268	.queue_setup		= mxc_isi_m2m_vb2_queue_setup,269	.buf_init		= mxc_isi_m2m_vb2_buffer_init,270	.buf_prepare		= mxc_isi_m2m_vb2_buffer_prepare,271	.buf_queue		= mxc_isi_m2m_vb2_buffer_queue,272	.wait_prepare		= vb2_ops_wait_prepare,273	.wait_finish		= vb2_ops_wait_finish,274	.start_streaming	= mxc_isi_m2m_vb2_start_streaming,275	.stop_streaming		= mxc_isi_m2m_vb2_stop_streaming,276};277 278static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq,279				  struct vb2_queue *dst_vq)280{281	struct mxc_isi_m2m_ctx *ctx = priv;282	struct mxc_isi_m2m *m2m = ctx->m2m;283	int ret;284 285	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;286	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;287	src_vq->drv_priv = ctx;288	src_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);289	src_vq->ops = &mxc_isi_m2m_vb2_qops;290	src_vq->mem_ops = &vb2_dma_contig_memops;291	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;292	src_vq->lock = &ctx->vb2_lock;293	src_vq->dev = m2m->isi->dev;294 295	ret = vb2_queue_init(src_vq);296	if (ret)297		return ret;298 299	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;300	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;301	dst_vq->drv_priv = ctx;302	dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);303	dst_vq->ops = &mxc_isi_m2m_vb2_qops;304	dst_vq->mem_ops = &vb2_dma_contig_memops;305	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;306	dst_vq->lock = &ctx->vb2_lock;307	dst_vq->dev = m2m->isi->dev;308 309	return vb2_queue_init(dst_vq);310}311 312/* -----------------------------------------------------------------------------313 * V4L2 controls314 */315 316static inline struct mxc_isi_m2m_ctx *317ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl)318{319	return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler);320}321 322static int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl)323{324	struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl);325 326	switch (ctrl->id) {327	case V4L2_CID_HFLIP:328		ctx->ctrls.hflip = ctrl->val;329		break;330 331	case V4L2_CID_VFLIP:332		ctx->ctrls.vflip = ctrl->val;333		break;334 335	case V4L2_CID_ALPHA_COMPONENT:336		ctx->ctrls.alpha = ctrl->val;337		break;338	}339 340	return 0;341}342 343static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = {344	.s_ctrl = mxc_isi_m2m_ctx_s_ctrl,345};346 347static int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx)348{349	struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler;350	int ret;351 352	v4l2_ctrl_handler_init(handler, 3);353 354	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,355			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);356	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,357			  V4L2_CID_HFLIP, 0, 1, 1, 0);358	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,359			  V4L2_CID_VFLIP, 0, 1, 1, 0);360 361	if (handler->error) {362		ret = handler->error;363		v4l2_ctrl_handler_free(handler);364		return ret;365	}366 367	ctx->fh.ctrl_handler = handler;368 369	return 0;370}371 372static void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx)373{374	v4l2_ctrl_handler_free(&ctx->ctrls.handler);375}376 377/* -----------------------------------------------------------------------------378 * V4L2 ioctls379 */380 381static int mxc_isi_m2m_querycap(struct file *file, void *fh,382				struct v4l2_capability *cap)383{384	strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));385	strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card));386	cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;387	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;388 389	return 0;390}391 392static int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh,393				    struct v4l2_fmtdesc *f)394{395	const enum mxc_isi_video_type type =396		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?397		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;398	const struct mxc_isi_format_info *info;399 400	info = mxc_isi_format_enum(f->index, type);401	if (!info)402		return -EINVAL;403 404	f->pixelformat = info->fourcc;405	f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC406		 |  V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;407 408	return 0;409}410 411static const struct mxc_isi_format_info *412__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx,413			  struct v4l2_pix_format_mplane *pix,414			  const enum mxc_isi_video_type type)415{416	if (type == MXC_ISI_VIDEO_M2M_CAP) {417		/* Downscaling only  */418		pix->width = min(pix->width, ctx->queues.out.format.width);419		pix->height = min(pix->height, ctx->queues.out.format.height);420	}421 422	return mxc_isi_format_try(ctx->m2m->pipe, pix, type);423}424 425static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh,426				   struct v4l2_format *f)427{428	const enum mxc_isi_video_type type =429		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?430		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;431	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);432 433	__mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type);434 435	return 0;436}437 438static int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh,439				 struct v4l2_format *f)440{441	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);442	const struct mxc_isi_m2m_ctx_queue_data *qdata =443		mxc_isi_m2m_ctx_qdata(ctx, f->type);444 445	f->fmt.pix_mp = qdata->format;446 447	return 0;448}449 450static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh,451				 struct v4l2_format *f)452{453	const enum mxc_isi_video_type type =454		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?455		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;456	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);457	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;458	const struct mxc_isi_format_info *info;459	struct vb2_queue *vq;460 461	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);462	if (!vq)463		return -EINVAL;464 465	if (vb2_is_busy(vq))466		return -EBUSY;467 468	info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type);469 470	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {471		ctx->queues.out.format = *pix;472		ctx->queues.out.info = info;473	}474 475	/*476	 * Always set the format on the capture side, due to either format477	 * propagation or direct setting.478	 */479	ctx->queues.cap.format = *pix;480	ctx->queues.cap.info = info;481 482	return 0;483}484 485static int mxc_isi_m2m_streamon(struct file *file, void *fh,486				enum v4l2_buf_type type)487{488	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);489	const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;490	const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;491	const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;492	const struct mxc_isi_format_info *out_info = ctx->queues.out.info;493	struct mxc_isi_m2m *m2m = ctx->m2m;494	bool bypass;495 496	int ret;497 498	mutex_lock(&m2m->lock);499 500	if (m2m->usage_count == INT_MAX) {501		ret = -EOVERFLOW;502		goto unlock;503	}504 505	bypass = cap_pix->width == out_pix->width &&506		 cap_pix->height == out_pix->height &&507		 cap_info->encoding == out_info->encoding;508 509	/*510	 * Acquire the pipe and initialize the channel with the first user of511	 * the M2M device.512	 */513	if (m2m->usage_count == 0) {514		ret = mxc_isi_channel_acquire(m2m->pipe,515					      &mxc_isi_m2m_frame_write_done,516					      bypass);517		if (ret)518			goto unlock;519 520		mxc_isi_channel_get(m2m->pipe);521	}522 523	m2m->usage_count++;524 525	/*526	 * Allocate resources for the channel, counting how many users require527	 * buffer chaining.528	 */529	if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {530		ret = mxc_isi_channel_chain(m2m->pipe, bypass);531		if (ret)532			goto deinit;533 534		m2m->chained_count++;535		ctx->chained = true;536	}537 538	/*539	 * Drop the lock to start the stream, as the .device_run() operation540	 * needs to acquire it.541	 */542	mutex_unlock(&m2m->lock);543	ret = v4l2_m2m_ioctl_streamon(file, fh, type);544	if (ret) {545		/* Reacquire the lock for the cleanup path. */546		mutex_lock(&m2m->lock);547		goto unchain;548	}549 550	return 0;551 552unchain:553	if (ctx->chained && --m2m->chained_count == 0)554		mxc_isi_channel_unchain(m2m->pipe);555	ctx->chained = false;556 557deinit:558	if (--m2m->usage_count == 0) {559		mxc_isi_channel_put(m2m->pipe);560		mxc_isi_channel_release(m2m->pipe);561	}562 563unlock:564	mutex_unlock(&m2m->lock);565	return ret;566}567 568static int mxc_isi_m2m_streamoff(struct file *file, void *fh,569				 enum v4l2_buf_type type)570{571	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);572	struct mxc_isi_m2m *m2m = ctx->m2m;573 574	v4l2_m2m_ioctl_streamoff(file, fh, type);575 576	mutex_lock(&m2m->lock);577 578	/*579	 * If the last context is this one, reset it to make sure the device580	 * will be reconfigured when streaming is restarted.581	 */582	if (m2m->last_ctx == ctx)583		m2m->last_ctx = NULL;584 585	/* Free the channel resources if this is the last chained context. */586	if (ctx->chained && --m2m->chained_count == 0)587		mxc_isi_channel_unchain(m2m->pipe);588	ctx->chained = false;589 590	/* Turn off the light with the last user. */591	if (--m2m->usage_count == 0) {592		mxc_isi_channel_disable(m2m->pipe);593		mxc_isi_channel_put(m2m->pipe);594		mxc_isi_channel_release(m2m->pipe);595	}596 597	WARN_ON(m2m->usage_count < 0);598 599	mutex_unlock(&m2m->lock);600 601	return 0;602}603 604static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = {605	.vidioc_querycap		= mxc_isi_m2m_querycap,606 607	.vidioc_enum_fmt_vid_cap	= mxc_isi_m2m_enum_fmt_vid,608	.vidioc_enum_fmt_vid_out	= mxc_isi_m2m_enum_fmt_vid,609	.vidioc_g_fmt_vid_cap_mplane	= mxc_isi_m2m_g_fmt_vid,610	.vidioc_g_fmt_vid_out_mplane	= mxc_isi_m2m_g_fmt_vid,611	.vidioc_s_fmt_vid_cap_mplane	= mxc_isi_m2m_s_fmt_vid,612	.vidioc_s_fmt_vid_out_mplane	= mxc_isi_m2m_s_fmt_vid,613	.vidioc_try_fmt_vid_cap_mplane	= mxc_isi_m2m_try_fmt_vid,614	.vidioc_try_fmt_vid_out_mplane	= mxc_isi_m2m_try_fmt_vid,615 616	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,617	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,618	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,619	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,620	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,621	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,622	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,623 624	.vidioc_streamon		= mxc_isi_m2m_streamon,625	.vidioc_streamoff		= mxc_isi_m2m_streamoff,626 627	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,628	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,629};630 631/* -----------------------------------------------------------------------------632 * Video device file operations633 */634 635static void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx,636				    struct mxc_isi_m2m_ctx_queue_data *qdata,637				    enum mxc_isi_video_type type)638{639	qdata->format.width = MXC_ISI_DEF_WIDTH;640	qdata->format.height = MXC_ISI_DEF_HEIGHT;641	qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT;642 643	qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type);644}645 646static int mxc_isi_m2m_open(struct file *file)647{648	struct video_device *vdev = video_devdata(file);649	struct mxc_isi_m2m *m2m = video_drvdata(file);650	struct mxc_isi_m2m_ctx *ctx;651	int ret;652 653	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);654	if (!ctx)655		return -ENOMEM;656 657	ctx->m2m = m2m;658	mutex_init(&ctx->vb2_lock);659 660	v4l2_fh_init(&ctx->fh, vdev);661	file->private_data = &ctx->fh;662 663	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx,664					    &mxc_isi_m2m_queue_init);665	if (IS_ERR(ctx->fh.m2m_ctx)) {666		ret = PTR_ERR(ctx->fh.m2m_ctx);667		ctx->fh.m2m_ctx = NULL;668		goto err_fh;669	}670 671	mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT);672	mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP);673 674	ret = mxc_isi_m2m_ctx_ctrls_create(ctx);675	if (ret)676		goto err_ctx;677 678	ret = pm_runtime_resume_and_get(m2m->isi->dev);679	if (ret)680		goto err_ctrls;681 682	v4l2_fh_add(&ctx->fh);683 684	return 0;685 686err_ctrls:687	mxc_isi_m2m_ctx_ctrls_delete(ctx);688err_ctx:689	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);690err_fh:691	v4l2_fh_exit(&ctx->fh);692	mutex_destroy(&ctx->vb2_lock);693	kfree(ctx);694	return ret;695}696 697static int mxc_isi_m2m_release(struct file *file)698{699	struct mxc_isi_m2m *m2m = video_drvdata(file);700	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(file->private_data);701 702	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);703	mxc_isi_m2m_ctx_ctrls_delete(ctx);704 705	v4l2_fh_del(&ctx->fh);706	v4l2_fh_exit(&ctx->fh);707 708	mutex_destroy(&ctx->vb2_lock);709	kfree(ctx);710 711	pm_runtime_put(m2m->isi->dev);712 713	return 0;714}715 716static const struct v4l2_file_operations mxc_isi_m2m_fops = {717	.owner		= THIS_MODULE,718	.open		= mxc_isi_m2m_open,719	.release	= mxc_isi_m2m_release,720	.poll		= v4l2_m2m_fop_poll,721	.unlocked_ioctl	= video_ioctl2,722	.mmap		= v4l2_m2m_fop_mmap,723};724 725/* -----------------------------------------------------------------------------726 * Registration727 */728 729int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev)730{731	struct mxc_isi_m2m *m2m = &isi->m2m;732	struct video_device *vdev = &m2m->vdev;733	struct media_link *link;734	int ret;735 736	m2m->isi = isi;737	m2m->pipe = &isi->pipes[0];738 739	mutex_init(&m2m->lock);740 741	/* Initialize the video device and create controls. */742	snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m");743 744	vdev->fops	= &mxc_isi_m2m_fops;745	vdev->ioctl_ops	= &mxc_isi_m2m_ioctl_ops;746	vdev->v4l2_dev	= v4l2_dev;747	vdev->minor	= -1;748	vdev->release	= video_device_release_empty;749	vdev->vfl_dir	= VFL_DIR_M2M;750 751	vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;752	video_set_drvdata(vdev, m2m);753 754	/* Create the M2M device. */755	m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops);756	if (IS_ERR(m2m->m2m_dev)) {757		dev_err(isi->dev, "failed to initialize m2m device\n");758		ret = PTR_ERR(m2m->m2m_dev);759		goto err_mutex;760	}761 762	/* Register the video device. */763	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);764	if (ret < 0) {765		dev_err(isi->dev, "failed to register m2m device\n");766		goto err_m2m;767	}768 769	/*770	 * Populate the media graph. We can't use the mem2mem helper771	 * v4l2_m2m_register_media_controller() as the M2M interface needs to772	 * be connected to the existing entities in the graph, so we have to773	 * wire things up manually:774	 *775	 * - The entity in the video_device, which isn't touched by the V4L2776	 *   core for M2M devices, is used as the source I/O entity in the777	 *   graph, connected to the crossbar switch.778	 *779	 * - The video device at the end of the pipeline provides the sink780	 *   entity, and is already wired up in the graph.781	 *782	 * - A new interface is created, pointing at both entities. The sink783	 *   entity will thus have two interfaces pointing to it.784	 */785	m2m->pad.flags = MEDIA_PAD_FL_SOURCE;786	vdev->entity.name = "mxc_isi.output";787	vdev->entity.function = MEDIA_ENT_F_IO_V4L;788	ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad);789	if (ret)790		goto err_video;791 792	ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity);793	if (ret)794		goto err_entity_cleanup;795 796	ret = media_create_pad_link(&vdev->entity, 0,797				    &m2m->isi->crossbar.sd.entity,798				    m2m->isi->crossbar.num_sinks - 1,799				    MEDIA_LNK_FL_IMMUTABLE |800				    MEDIA_LNK_FL_ENABLED);801	if (ret)802		goto err_entity_unreg;803 804	m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO,805					 0, VIDEO_MAJOR, vdev->minor);806	if (!m2m->intf) {807		ret = -ENOMEM;808		goto err_entity_unreg;809	}810 811	link = media_create_intf_link(&vdev->entity, &m2m->intf->intf,812				      MEDIA_LNK_FL_IMMUTABLE |813				      MEDIA_LNK_FL_ENABLED);814	if (!link) {815		ret = -ENOMEM;816		goto err_devnode;817	}818 819	link = media_create_intf_link(&m2m->pipe->video.vdev.entity,820				      &m2m->intf->intf,821				      MEDIA_LNK_FL_IMMUTABLE |822				      MEDIA_LNK_FL_ENABLED);823	if (!link) {824		ret = -ENOMEM;825		goto err_devnode;826	}827 828	return 0;829 830err_devnode:831	media_devnode_remove(m2m->intf);832err_entity_unreg:833	media_device_unregister_entity(&vdev->entity);834err_entity_cleanup:835	media_entity_cleanup(&vdev->entity);836err_video:837	video_unregister_device(vdev);838err_m2m:839	v4l2_m2m_release(m2m->m2m_dev);840err_mutex:841	mutex_destroy(&m2m->lock);842	return ret;843}844 845int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi)846{847	struct mxc_isi_m2m *m2m = &isi->m2m;848	struct video_device *vdev = &m2m->vdev;849 850	video_unregister_device(vdev);851 852	v4l2_m2m_release(m2m->m2m_dev);853	media_devnode_remove(m2m->intf);854	media_entity_cleanup(&vdev->entity);855	mutex_destroy(&m2m->lock);856 857	return 0;858}859