162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which 662306a36Sopenharmony_ci * used to process image from camera sensor or memory to memory or DC 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2019 NXP Semiconductor 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/container_of.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/limits.h> 1662306a36Sopenharmony_ci#include <linux/minmax.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/types.h> 2362306a36Sopenharmony_ci#include <linux/videodev2.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <media/media-entity.h> 2662306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2762306a36Sopenharmony_ci#include <media/v4l2-device.h> 2862306a36Sopenharmony_ci#include <media/v4l2-event.h> 2962306a36Sopenharmony_ci#include <media/v4l2-fh.h> 3062306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3162306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 3262306a36Sopenharmony_ci#include <media/videobuf2-core.h> 3362306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "imx8-isi-core.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct mxc_isi_m2m_buffer { 3862306a36Sopenharmony_ci struct v4l2_m2m_buffer buf; 3962306a36Sopenharmony_ci dma_addr_t dma_addrs[3]; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct mxc_isi_m2m_ctx_queue_data { 4362306a36Sopenharmony_ci struct v4l2_pix_format_mplane format; 4462306a36Sopenharmony_ci const struct mxc_isi_format_info *info; 4562306a36Sopenharmony_ci u32 sequence; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct mxc_isi_m2m_ctx { 4962306a36Sopenharmony_ci struct v4l2_fh fh; 5062306a36Sopenharmony_ci struct mxc_isi_m2m *m2m; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Protects the m2m vb2 queues */ 5362306a36Sopenharmony_ci struct mutex vb2_lock; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct { 5662306a36Sopenharmony_ci struct mxc_isi_m2m_ctx_queue_data out; 5762306a36Sopenharmony_ci struct mxc_isi_m2m_ctx_queue_data cap; 5862306a36Sopenharmony_ci } queues; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci struct { 6162306a36Sopenharmony_ci struct v4l2_ctrl_handler handler; 6262306a36Sopenharmony_ci unsigned int alpha; 6362306a36Sopenharmony_ci bool hflip; 6462306a36Sopenharmony_ci bool vflip; 6562306a36Sopenharmony_ci } ctrls; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci bool chained; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline struct mxc_isi_m2m_buffer * 7162306a36Sopenharmony_cito_isi_m2m_buffer(struct vb2_v4l2_buffer *buf) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline struct mxc_isi_m2m_ctx *to_isi_m2m_ctx(struct v4l2_fh *fh) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return container_of(fh, struct mxc_isi_m2m_ctx, fh); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline struct mxc_isi_m2m_ctx_queue_data * 8262306a36Sopenharmony_cimxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx *ctx, enum v4l2_buf_type type) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) 8562306a36Sopenharmony_ci return &ctx->queues.out; 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci return &ctx->queues.cap; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 9162306a36Sopenharmony_ci * V4L2 M2M device operations 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = &pipe->isi->m2m; 9762306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; 9862306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(m2m->m2m_dev); 10162306a36Sopenharmony_ci if (!ctx) { 10262306a36Sopenharmony_ci dev_err(m2m->isi->dev, 10362306a36Sopenharmony_ci "Instance released before the end of transaction\n"); 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 10862306a36Sopenharmony_ci dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, false); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci src_vbuf->sequence = ctx->queues.out.sequence++; 11362306a36Sopenharmony_ci dst_vbuf->sequence = ctx->queues.cap.sequence++; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_DONE); 11662306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_DONE); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci v4l2_m2m_job_finish(m2m->m2m_dev, ctx->fh.m2m_ctx); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void mxc_isi_m2m_device_run(void *priv) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = priv; 12462306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = ctx->m2m; 12562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; 12662306a36Sopenharmony_ci struct mxc_isi_m2m_buffer *src_buf, *dst_buf; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci mxc_isi_channel_disable(m2m->pipe); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci mutex_lock(&m2m->lock); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* If the context has changed, reconfigure the channel. */ 13362306a36Sopenharmony_ci if (m2m->last_ctx != ctx) { 13462306a36Sopenharmony_ci const struct v4l2_area in_size = { 13562306a36Sopenharmony_ci .width = ctx->queues.out.format.width, 13662306a36Sopenharmony_ci .height = ctx->queues.out.format.height, 13762306a36Sopenharmony_ci }; 13862306a36Sopenharmony_ci const struct v4l2_area scale = { 13962306a36Sopenharmony_ci .width = ctx->queues.cap.format.width, 14062306a36Sopenharmony_ci .height = ctx->queues.cap.format.height, 14162306a36Sopenharmony_ci }; 14262306a36Sopenharmony_ci const struct v4l2_rect crop = { 14362306a36Sopenharmony_ci .width = ctx->queues.cap.format.width, 14462306a36Sopenharmony_ci .height = ctx->queues.cap.format.height, 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, 14862306a36Sopenharmony_ci &in_size, &scale, &crop, 14962306a36Sopenharmony_ci ctx->queues.out.info->encoding, 15062306a36Sopenharmony_ci ctx->queues.cap.info->encoding); 15162306a36Sopenharmony_ci mxc_isi_channel_set_input_format(m2m->pipe, 15262306a36Sopenharmony_ci ctx->queues.out.info, 15362306a36Sopenharmony_ci &ctx->queues.out.format); 15462306a36Sopenharmony_ci mxc_isi_channel_set_output_format(m2m->pipe, 15562306a36Sopenharmony_ci ctx->queues.cap.info, 15662306a36Sopenharmony_ci &ctx->queues.cap.format); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci m2m->last_ctx = ctx; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci mutex_unlock(&m2m->lock); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci mutex_lock(ctx->ctrls.handler.lock); 16462306a36Sopenharmony_ci mxc_isi_channel_set_alpha(m2m->pipe, ctx->ctrls.alpha); 16562306a36Sopenharmony_ci mxc_isi_channel_set_flip(m2m->pipe, ctx->ctrls.hflip, ctx->ctrls.vflip); 16662306a36Sopenharmony_ci mutex_unlock(ctx->ctrls.handler.lock); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci src_vbuf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 16962306a36Sopenharmony_ci dst_vbuf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci src_buf = to_isi_m2m_buffer(src_vbuf); 17262306a36Sopenharmony_ci dst_buf = to_isi_m2m_buffer(dst_vbuf); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci mxc_isi_channel_set_inbuf(m2m->pipe, src_buf->dma_addrs[0]); 17562306a36Sopenharmony_ci mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF1); 17662306a36Sopenharmony_ci mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF2); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci mxc_isi_channel_enable(m2m->pipe); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mxc_isi_channel_m2m_start(m2m->pipe); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct v4l2_m2m_ops mxc_isi_m2m_ops = { 18462306a36Sopenharmony_ci .device_run = mxc_isi_m2m_device_run, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 18862306a36Sopenharmony_ci * videobuf2 queue operations 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int mxc_isi_m2m_vb2_queue_setup(struct vb2_queue *q, 19262306a36Sopenharmony_ci unsigned int *num_buffers, 19362306a36Sopenharmony_ci unsigned int *num_planes, 19462306a36Sopenharmony_ci unsigned int sizes[], 19562306a36Sopenharmony_ci struct device *alloc_devs[]) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 19862306a36Sopenharmony_ci const struct mxc_isi_m2m_ctx_queue_data *qdata = 19962306a36Sopenharmony_ci mxc_isi_m2m_ctx_qdata(ctx, q->type); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return mxc_isi_video_queue_setup(&qdata->format, qdata->info, 20262306a36Sopenharmony_ci num_buffers, num_planes, sizes); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer *vb2) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct vb2_queue *vq = vb2->vb2_queue; 20862306a36Sopenharmony_ci struct mxc_isi_m2m_buffer *buf = to_isi_m2m_buffer(to_vb2_v4l2_buffer(vb2)); 20962306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); 21062306a36Sopenharmony_ci const struct mxc_isi_m2m_ctx_queue_data *qdata = 21162306a36Sopenharmony_ci mxc_isi_m2m_ctx_qdata(ctx, vq->type); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mxc_isi_video_buffer_init(vb2, buf->dma_addrs, qdata->info, 21462306a36Sopenharmony_ci &qdata->format); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer *vb2) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct vb2_queue *vq = vb2->vb2_queue; 22262306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vq); 22362306a36Sopenharmony_ci const struct mxc_isi_m2m_ctx_queue_data *qdata = 22462306a36Sopenharmony_ci mxc_isi_m2m_ctx_qdata(ctx, vq->type); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return mxc_isi_video_buffer_prepare(ctx->m2m->isi, vb2, qdata->info, 22762306a36Sopenharmony_ci &qdata->format); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); 23362306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q, 23962306a36Sopenharmony_ci unsigned int count) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 24262306a36Sopenharmony_ci struct mxc_isi_m2m_ctx_queue_data *qdata = 24362306a36Sopenharmony_ci mxc_isi_m2m_ctx_qdata(ctx, q->type); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci qdata->sequence = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 25362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (;;) { 25662306a36Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 25762306a36Sopenharmony_ci vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 26062306a36Sopenharmony_ci if (!vbuf) 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const struct vb2_ops mxc_isi_m2m_vb2_qops = { 26862306a36Sopenharmony_ci .queue_setup = mxc_isi_m2m_vb2_queue_setup, 26962306a36Sopenharmony_ci .buf_init = mxc_isi_m2m_vb2_buffer_init, 27062306a36Sopenharmony_ci .buf_prepare = mxc_isi_m2m_vb2_buffer_prepare, 27162306a36Sopenharmony_ci .buf_queue = mxc_isi_m2m_vb2_buffer_queue, 27262306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 27362306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 27462306a36Sopenharmony_ci .start_streaming = mxc_isi_m2m_vb2_start_streaming, 27562306a36Sopenharmony_ci .stop_streaming = mxc_isi_m2m_vb2_stop_streaming, 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq, 27962306a36Sopenharmony_ci struct vb2_queue *dst_vq) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = priv; 28262306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = ctx->m2m; 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 28662306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 28762306a36Sopenharmony_ci src_vq->drv_priv = ctx; 28862306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer); 28962306a36Sopenharmony_ci src_vq->ops = &mxc_isi_m2m_vb2_qops; 29062306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 29162306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 29262306a36Sopenharmony_ci src_vq->lock = &ctx->vb2_lock; 29362306a36Sopenharmony_ci src_vq->dev = m2m->isi->dev; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 30062306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 30162306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 30262306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer); 30362306a36Sopenharmony_ci dst_vq->ops = &mxc_isi_m2m_vb2_qops; 30462306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 30562306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 30662306a36Sopenharmony_ci dst_vq->lock = &ctx->vb2_lock; 30762306a36Sopenharmony_ci dst_vq->dev = m2m->isi->dev; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 31362306a36Sopenharmony_ci * V4L2 controls 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic inline struct mxc_isi_m2m_ctx * 31762306a36Sopenharmony_cictrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci switch (ctrl->id) { 32762306a36Sopenharmony_ci case V4L2_CID_HFLIP: 32862306a36Sopenharmony_ci ctx->ctrls.hflip = ctrl->val; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case V4L2_CID_VFLIP: 33262306a36Sopenharmony_ci ctx->ctrls.vflip = ctrl->val; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci case V4L2_CID_ALPHA_COMPONENT: 33662306a36Sopenharmony_ci ctx->ctrls.alpha = ctrl->val; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = { 34462306a36Sopenharmony_ci .s_ctrl = mxc_isi_m2m_ctx_s_ctrl, 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler; 35062306a36Sopenharmony_ci int ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci v4l2_ctrl_handler_init(handler, 3); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 35562306a36Sopenharmony_ci V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); 35662306a36Sopenharmony_ci v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 35762306a36Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 35862306a36Sopenharmony_ci v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 35962306a36Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (handler->error) { 36262306a36Sopenharmony_ci ret = handler->error; 36362306a36Sopenharmony_ci v4l2_ctrl_handler_free(handler); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ctx->fh.ctrl_handler = handler; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrls.handler); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 37862306a36Sopenharmony_ci * V4L2 ioctls 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int mxc_isi_m2m_querycap(struct file *file, void *fh, 38262306a36Sopenharmony_ci struct v4l2_capability *cap) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver)); 38562306a36Sopenharmony_ci strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card)); 38662306a36Sopenharmony_ci cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; 38762306a36Sopenharmony_ci cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh, 39362306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci const enum mxc_isi_video_type type = 39662306a36Sopenharmony_ci f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 39762306a36Sopenharmony_ci MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 39862306a36Sopenharmony_ci const struct mxc_isi_format_info *info; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci info = mxc_isi_format_enum(f->index, type); 40162306a36Sopenharmony_ci if (!info) 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci f->pixelformat = info->fourcc; 40562306a36Sopenharmony_ci f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC 40662306a36Sopenharmony_ci | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const struct mxc_isi_format_info * 41262306a36Sopenharmony_ci__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, 41362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix, 41462306a36Sopenharmony_ci const enum mxc_isi_video_type type) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci if (type == MXC_ISI_VIDEO_M2M_CAP) { 41762306a36Sopenharmony_ci /* Downscaling only */ 41862306a36Sopenharmony_ci pix->width = min(pix->width, ctx->queues.out.format.width); 41962306a36Sopenharmony_ci pix->height = min(pix->height, ctx->queues.out.format.height); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return mxc_isi_format_try(ctx->m2m->pipe, pix, type); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh, 42662306a36Sopenharmony_ci struct v4l2_format *f) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci const enum mxc_isi_video_type type = 42962306a36Sopenharmony_ci f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 43062306a36Sopenharmony_ci MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 43162306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci __mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh, 43962306a36Sopenharmony_ci struct v4l2_format *f) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); 44262306a36Sopenharmony_ci const struct mxc_isi_m2m_ctx_queue_data *qdata = 44362306a36Sopenharmony_ci mxc_isi_m2m_ctx_qdata(ctx, f->type); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci f->fmt.pix_mp = qdata->format; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, 45162306a36Sopenharmony_ci struct v4l2_format *f) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci const enum mxc_isi_video_type type = 45462306a36Sopenharmony_ci f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 45562306a36Sopenharmony_ci MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 45662306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); 45762306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 45862306a36Sopenharmony_ci const struct mxc_isi_format_info *info; 45962306a36Sopenharmony_ci struct vb2_queue *vq; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 46262306a36Sopenharmony_ci if (!vq) 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (vb2_is_busy(vq)) 46662306a36Sopenharmony_ci return -EBUSY; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 47162306a36Sopenharmony_ci ctx->queues.out.format = *pix; 47262306a36Sopenharmony_ci ctx->queues.out.info = info; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Always set the format on the capture side, due to either format 47762306a36Sopenharmony_ci * propagation or direct setting. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci ctx->queues.cap.format = *pix; 48062306a36Sopenharmony_ci ctx->queues.cap.info = info; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int mxc_isi_m2m_streamon(struct file *file, void *fh, 48662306a36Sopenharmony_ci enum v4l2_buf_type type) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); 48962306a36Sopenharmony_ci const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; 49062306a36Sopenharmony_ci const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; 49162306a36Sopenharmony_ci const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; 49262306a36Sopenharmony_ci const struct mxc_isi_format_info *out_info = ctx->queues.out.info; 49362306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = ctx->m2m; 49462306a36Sopenharmony_ci bool bypass; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci int ret; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci mutex_lock(&m2m->lock); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (m2m->usage_count == INT_MAX) { 50162306a36Sopenharmony_ci ret = -EOVERFLOW; 50262306a36Sopenharmony_ci goto unlock; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci bypass = cap_pix->width == out_pix->width && 50662306a36Sopenharmony_ci cap_pix->height == out_pix->height && 50762306a36Sopenharmony_ci cap_info->encoding == out_info->encoding; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * Acquire the pipe and initialize the channel with the first user of 51162306a36Sopenharmony_ci * the M2M device. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (m2m->usage_count == 0) { 51462306a36Sopenharmony_ci ret = mxc_isi_channel_acquire(m2m->pipe, 51562306a36Sopenharmony_ci &mxc_isi_m2m_frame_write_done, 51662306a36Sopenharmony_ci bypass); 51762306a36Sopenharmony_ci if (ret) 51862306a36Sopenharmony_ci goto unlock; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci mxc_isi_channel_get(m2m->pipe); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci m2m->usage_count++; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * Allocate resources for the channel, counting how many users require 52762306a36Sopenharmony_ci * buffer chaining. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { 53062306a36Sopenharmony_ci ret = mxc_isi_channel_chain(m2m->pipe, bypass); 53162306a36Sopenharmony_ci if (ret) 53262306a36Sopenharmony_ci goto deinit; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci m2m->chained_count++; 53562306a36Sopenharmony_ci ctx->chained = true; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* 53962306a36Sopenharmony_ci * Drop the lock to start the stream, as the .device_run() operation 54062306a36Sopenharmony_ci * needs to acquire it. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci mutex_unlock(&m2m->lock); 54362306a36Sopenharmony_ci ret = v4l2_m2m_ioctl_streamon(file, fh, type); 54462306a36Sopenharmony_ci if (ret) { 54562306a36Sopenharmony_ci /* Reacquire the lock for the cleanup path. */ 54662306a36Sopenharmony_ci mutex_lock(&m2m->lock); 54762306a36Sopenharmony_ci goto unchain; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciunchain: 55362306a36Sopenharmony_ci if (ctx->chained && --m2m->chained_count == 0) 55462306a36Sopenharmony_ci mxc_isi_channel_unchain(m2m->pipe); 55562306a36Sopenharmony_ci ctx->chained = false; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cideinit: 55862306a36Sopenharmony_ci if (--m2m->usage_count == 0) { 55962306a36Sopenharmony_ci mxc_isi_channel_put(m2m->pipe); 56062306a36Sopenharmony_ci mxc_isi_channel_release(m2m->pipe); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciunlock: 56462306a36Sopenharmony_ci mutex_unlock(&m2m->lock); 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int mxc_isi_m2m_streamoff(struct file *file, void *fh, 56962306a36Sopenharmony_ci enum v4l2_buf_type type) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); 57262306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = ctx->m2m; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci v4l2_m2m_ioctl_streamoff(file, fh, type); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci mutex_lock(&m2m->lock); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * If the last context is this one, reset it to make sure the device 58062306a36Sopenharmony_ci * will be reconfigured when streaming is restarted. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci if (m2m->last_ctx == ctx) 58362306a36Sopenharmony_ci m2m->last_ctx = NULL; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Free the channel resources if this is the last chained context. */ 58662306a36Sopenharmony_ci if (ctx->chained && --m2m->chained_count == 0) 58762306a36Sopenharmony_ci mxc_isi_channel_unchain(m2m->pipe); 58862306a36Sopenharmony_ci ctx->chained = false; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Turn off the light with the last user. */ 59162306a36Sopenharmony_ci if (--m2m->usage_count == 0) { 59262306a36Sopenharmony_ci mxc_isi_channel_disable(m2m->pipe); 59362306a36Sopenharmony_ci mxc_isi_channel_put(m2m->pipe); 59462306a36Sopenharmony_ci mxc_isi_channel_release(m2m->pipe); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci WARN_ON(m2m->usage_count < 0); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci mutex_unlock(&m2m->lock); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { 60562306a36Sopenharmony_ci .vidioc_querycap = mxc_isi_m2m_querycap, 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = mxc_isi_m2m_enum_fmt_vid, 60862306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = mxc_isi_m2m_enum_fmt_vid, 60962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = mxc_isi_m2m_g_fmt_vid, 61062306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = mxc_isi_m2m_g_fmt_vid, 61162306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = mxc_isi_m2m_s_fmt_vid, 61262306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = mxc_isi_m2m_s_fmt_vid, 61362306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = mxc_isi_m2m_try_fmt_vid, 61462306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = mxc_isi_m2m_try_fmt_vid, 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 61762306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 61862306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 61962306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 62062306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 62162306a36Sopenharmony_ci .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 62262306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci .vidioc_streamon = mxc_isi_m2m_streamon, 62562306a36Sopenharmony_ci .vidioc_streamoff = mxc_isi_m2m_streamoff, 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 62862306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 63262306a36Sopenharmony_ci * Video device file operations 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx, 63662306a36Sopenharmony_ci struct mxc_isi_m2m_ctx_queue_data *qdata, 63762306a36Sopenharmony_ci enum mxc_isi_video_type type) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci qdata->format.width = MXC_ISI_DEF_WIDTH; 64062306a36Sopenharmony_ci qdata->format.height = MXC_ISI_DEF_HEIGHT; 64162306a36Sopenharmony_ci qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int mxc_isi_m2m_open(struct file *file) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 64962306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = video_drvdata(file); 65062306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx; 65162306a36Sopenharmony_ci int ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 65462306a36Sopenharmony_ci if (!ctx) 65562306a36Sopenharmony_ci return -ENOMEM; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ctx->m2m = m2m; 65862306a36Sopenharmony_ci mutex_init(&ctx->vb2_lock); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, vdev); 66162306a36Sopenharmony_ci file->private_data = &ctx->fh; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx, 66462306a36Sopenharmony_ci &mxc_isi_m2m_queue_init); 66562306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 66662306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 66762306a36Sopenharmony_ci ctx->fh.m2m_ctx = NULL; 66862306a36Sopenharmony_ci goto err_fh; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT); 67262306a36Sopenharmony_ci mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = mxc_isi_m2m_ctx_ctrls_create(ctx); 67562306a36Sopenharmony_ci if (ret) 67662306a36Sopenharmony_ci goto err_ctx; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(m2m->isi->dev); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci goto err_ctrls; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cierr_ctrls: 68762306a36Sopenharmony_ci mxc_isi_m2m_ctx_ctrls_delete(ctx); 68862306a36Sopenharmony_cierr_ctx: 68962306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 69062306a36Sopenharmony_cierr_fh: 69162306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 69262306a36Sopenharmony_ci mutex_destroy(&ctx->vb2_lock); 69362306a36Sopenharmony_ci kfree(ctx); 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int mxc_isi_m2m_release(struct file *file) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = video_drvdata(file); 70062306a36Sopenharmony_ci struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(file->private_data); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 70362306a36Sopenharmony_ci mxc_isi_m2m_ctx_ctrls_delete(ctx); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 70662306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci mutex_destroy(&ctx->vb2_lock); 70962306a36Sopenharmony_ci kfree(ctx); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci pm_runtime_put(m2m->isi->dev); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic const struct v4l2_file_operations mxc_isi_m2m_fops = { 71762306a36Sopenharmony_ci .owner = THIS_MODULE, 71862306a36Sopenharmony_ci .open = mxc_isi_m2m_open, 71962306a36Sopenharmony_ci .release = mxc_isi_m2m_release, 72062306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 72162306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 72262306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 72362306a36Sopenharmony_ci}; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 72662306a36Sopenharmony_ci * Registration 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciint mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = &isi->m2m; 73262306a36Sopenharmony_ci struct video_device *vdev = &m2m->vdev; 73362306a36Sopenharmony_ci struct media_link *link; 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci m2m->isi = isi; 73762306a36Sopenharmony_ci m2m->pipe = &isi->pipes[0]; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci mutex_init(&m2m->lock); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Initialize the video device and create controls. */ 74262306a36Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m"); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci vdev->fops = &mxc_isi_m2m_fops; 74562306a36Sopenharmony_ci vdev->ioctl_ops = &mxc_isi_m2m_ioctl_ops; 74662306a36Sopenharmony_ci vdev->v4l2_dev = v4l2_dev; 74762306a36Sopenharmony_ci vdev->minor = -1; 74862306a36Sopenharmony_ci vdev->release = video_device_release_empty; 74962306a36Sopenharmony_ci vdev->vfl_dir = VFL_DIR_M2M; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; 75262306a36Sopenharmony_ci video_set_drvdata(vdev, m2m); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Create the M2M device. */ 75562306a36Sopenharmony_ci m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops); 75662306a36Sopenharmony_ci if (IS_ERR(m2m->m2m_dev)) { 75762306a36Sopenharmony_ci dev_err(isi->dev, "failed to initialize m2m device\n"); 75862306a36Sopenharmony_ci ret = PTR_ERR(m2m->m2m_dev); 75962306a36Sopenharmony_ci goto err_mutex; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Register the video device. */ 76362306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 76462306a36Sopenharmony_ci if (ret < 0) { 76562306a36Sopenharmony_ci dev_err(isi->dev, "failed to register m2m device\n"); 76662306a36Sopenharmony_ci goto err_m2m; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * Populate the media graph. We can't use the mem2mem helper 77162306a36Sopenharmony_ci * v4l2_m2m_register_media_controller() as the M2M interface needs to 77262306a36Sopenharmony_ci * be connected to the existing entities in the graph, so we have to 77362306a36Sopenharmony_ci * wire things up manually: 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * - The entity in the video_device, which isn't touched by the V4L2 77662306a36Sopenharmony_ci * core for M2M devices, is used as the source I/O entity in the 77762306a36Sopenharmony_ci * graph, connected to the crossbar switch. 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * - The video device at the end of the pipeline provides the sink 78062306a36Sopenharmony_ci * entity, and is already wired up in the graph. 78162306a36Sopenharmony_ci * 78262306a36Sopenharmony_ci * - A new interface is created, pointing at both entities. The sink 78362306a36Sopenharmony_ci * entity will thus have two interfaces pointing to it. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci m2m->pad.flags = MEDIA_PAD_FL_SOURCE; 78662306a36Sopenharmony_ci vdev->entity.name = "mxc_isi.output"; 78762306a36Sopenharmony_ci vdev->entity.function = MEDIA_ENT_F_IO_V4L; 78862306a36Sopenharmony_ci ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad); 78962306a36Sopenharmony_ci if (ret) 79062306a36Sopenharmony_ci goto err_video; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity); 79362306a36Sopenharmony_ci if (ret) 79462306a36Sopenharmony_ci goto err_entity_cleanup; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = media_create_pad_link(&vdev->entity, 0, 79762306a36Sopenharmony_ci &m2m->isi->crossbar.sd.entity, 79862306a36Sopenharmony_ci m2m->isi->crossbar.num_sinks - 1, 79962306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 80062306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 80162306a36Sopenharmony_ci if (ret) 80262306a36Sopenharmony_ci goto err_entity_unreg; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO, 80562306a36Sopenharmony_ci 0, VIDEO_MAJOR, vdev->minor); 80662306a36Sopenharmony_ci if (!m2m->intf) { 80762306a36Sopenharmony_ci ret = -ENOMEM; 80862306a36Sopenharmony_ci goto err_entity_unreg; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci link = media_create_intf_link(&vdev->entity, &m2m->intf->intf, 81262306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 81362306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 81462306a36Sopenharmony_ci if (!link) { 81562306a36Sopenharmony_ci ret = -ENOMEM; 81662306a36Sopenharmony_ci goto err_devnode; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci link = media_create_intf_link(&m2m->pipe->video.vdev.entity, 82062306a36Sopenharmony_ci &m2m->intf->intf, 82162306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 82262306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 82362306a36Sopenharmony_ci if (!link) { 82462306a36Sopenharmony_ci ret = -ENOMEM; 82562306a36Sopenharmony_ci goto err_devnode; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cierr_devnode: 83162306a36Sopenharmony_ci media_devnode_remove(m2m->intf); 83262306a36Sopenharmony_cierr_entity_unreg: 83362306a36Sopenharmony_ci media_device_unregister_entity(&vdev->entity); 83462306a36Sopenharmony_cierr_entity_cleanup: 83562306a36Sopenharmony_ci media_entity_cleanup(&vdev->entity); 83662306a36Sopenharmony_cierr_video: 83762306a36Sopenharmony_ci video_unregister_device(vdev); 83862306a36Sopenharmony_cierr_m2m: 83962306a36Sopenharmony_ci v4l2_m2m_release(m2m->m2m_dev); 84062306a36Sopenharmony_cierr_mutex: 84162306a36Sopenharmony_ci mutex_destroy(&m2m->lock); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ciint mxc_isi_m2m_unregister(struct mxc_isi_dev *isi) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct mxc_isi_m2m *m2m = &isi->m2m; 84862306a36Sopenharmony_ci struct video_device *vdev = &m2m->vdev; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci video_unregister_device(vdev); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci v4l2_m2m_release(m2m->m2m_dev); 85362306a36Sopenharmony_ci media_devnode_remove(m2m->intf); 85462306a36Sopenharmony_ci media_entity_cleanup(&vdev->entity); 85562306a36Sopenharmony_ci mutex_destroy(&m2m->lock); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 859