162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/bug.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/clk.h> 2262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2362306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2462306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "common.h" 2762306a36Sopenharmony_ci#include "fimc-core.h" 2862306a36Sopenharmony_ci#include "fimc-reg.h" 2962306a36Sopenharmony_ci#include "media-dev.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic unsigned int get_m2m_fmt_flags(unsigned int stream_type) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 3462306a36Sopenharmony_ci return FMT_FLAGS_M2M_IN; 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci return FMT_FLAGS_M2M_OUT; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!ctx || !ctx->fh.m2m_ctx) 4462306a36Sopenharmony_ci return; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 4762306a36Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (src_vb) 5062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_vb, vb_state); 5162306a36Sopenharmony_ci if (dst_vb) 5262306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, vb_state); 5362306a36Sopenharmony_ci if (src_vb && dst_vb) 5462306a36Sopenharmony_ci v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, 5562306a36Sopenharmony_ci ctx->fh.m2m_ctx); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Complete the transaction which has been scheduled for execution. */ 5962306a36Sopenharmony_cistatic void fimc_m2m_shutdown(struct fimc_ctx *ctx) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!fimc_m2m_pending(fimc)) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci wait_event_timeout(fimc->irq_queue, 6962306a36Sopenharmony_ci !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), 7062306a36Sopenharmony_ci FIMC_SHUTDOWN_TIMEOUT); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct fimc_ctx *ctx = q->drv_priv; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return pm_runtime_resume_and_get(&ctx->fimc_dev->pdev->dev); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct fimc_ctx *ctx = q->drv_priv; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci fimc_m2m_shutdown(ctx); 8562306a36Sopenharmony_ci fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); 8662306a36Sopenharmony_ci pm_runtime_put(&ctx->fimc_dev->pdev->dev); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void fimc_device_run(void *priv) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 9262306a36Sopenharmony_ci struct fimc_ctx *ctx = priv; 9362306a36Sopenharmony_ci struct fimc_frame *sf, *df; 9462306a36Sopenharmony_ci struct fimc_dev *fimc; 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci int ret; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (WARN(!ctx, "Null context\n")) 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci fimc = ctx->fimc_dev; 10262306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci set_bit(ST_M2M_PEND, &fimc->state); 10562306a36Sopenharmony_ci sf = &ctx->s_frame; 10662306a36Sopenharmony_ci df = &ctx->d_frame; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (ctx->state & FIMC_PARAMS) { 10962306a36Sopenharmony_ci /* Prepare the DMA offsets for scaler */ 11062306a36Sopenharmony_ci fimc_prepare_dma_offset(ctx, sf); 11162306a36Sopenharmony_ci fimc_prepare_dma_offset(ctx, df); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 11562306a36Sopenharmony_ci ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->addr); 11662306a36Sopenharmony_ci if (ret) 11762306a36Sopenharmony_ci goto dma_unlock; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 12062306a36Sopenharmony_ci ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->addr); 12162306a36Sopenharmony_ci if (ret) 12262306a36Sopenharmony_ci goto dma_unlock; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 12562306a36Sopenharmony_ci dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 12662306a36Sopenharmony_ci dst_vb->flags |= 12762306a36Sopenharmony_ci src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Reconfigure hardware if the context has changed. */ 13062306a36Sopenharmony_ci if (fimc->m2m.ctx != ctx) { 13162306a36Sopenharmony_ci ctx->state |= FIMC_PARAMS; 13262306a36Sopenharmony_ci fimc->m2m.ctx = ctx; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (ctx->state & FIMC_PARAMS) { 13662306a36Sopenharmony_ci fimc_set_yuv_order(ctx); 13762306a36Sopenharmony_ci fimc_hw_set_input_path(ctx); 13862306a36Sopenharmony_ci fimc_hw_set_in_dma(ctx); 13962306a36Sopenharmony_ci ret = fimc_set_scaler_info(ctx); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci goto dma_unlock; 14262306a36Sopenharmony_ci fimc_hw_set_prescaler(ctx); 14362306a36Sopenharmony_ci fimc_hw_set_mainscaler(ctx); 14462306a36Sopenharmony_ci fimc_hw_set_target_format(ctx); 14562306a36Sopenharmony_ci fimc_hw_set_rotation(ctx); 14662306a36Sopenharmony_ci fimc_hw_set_effect(ctx); 14762306a36Sopenharmony_ci fimc_hw_set_out_dma(ctx); 14862306a36Sopenharmony_ci if (fimc->drv_data->alpha_color) 14962306a36Sopenharmony_ci fimc_hw_set_rgb_alpha(ctx); 15062306a36Sopenharmony_ci fimc_hw_set_output_path(ctx); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci fimc_hw_set_input_addr(fimc, &sf->addr); 15362306a36Sopenharmony_ci fimc_hw_set_output_addr(fimc, &df->addr, -1); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci fimc_activate_capture(ctx); 15662306a36Sopenharmony_ci ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); 15762306a36Sopenharmony_ci fimc_hw_activate_input_dma(fimc, true); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cidma_unlock: 16062306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void fimc_job_abort(void *priv) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci fimc_m2m_shutdown(priv); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int fimc_queue_setup(struct vb2_queue *vq, 16962306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 17062306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct fimc_ctx *ctx = vb2_get_drv_priv(vq); 17362306a36Sopenharmony_ci struct fimc_frame *f; 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci f = ctx_get_frame(ctx, vq->type); 17762306a36Sopenharmony_ci if (IS_ERR(f)) 17862306a36Sopenharmony_ci return PTR_ERR(f); 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Return number of non-contiguous planes (plane buffers) 18162306a36Sopenharmony_ci * depending on the configured color format. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (!f->fmt) 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *num_planes = f->fmt->memplanes; 18762306a36Sopenharmony_ci for (i = 0; i < f->fmt->memplanes; i++) 18862306a36Sopenharmony_ci sizes[i] = f->payload[i]; 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int fimc_buf_prepare(struct vb2_buffer *vb) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 19562306a36Sopenharmony_ci struct fimc_frame *frame; 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci frame = ctx_get_frame(ctx, vb->vb2_queue->type); 19962306a36Sopenharmony_ci if (IS_ERR(frame)) 20062306a36Sopenharmony_ci return PTR_ERR(frame); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci for (i = 0; i < frame->fmt->memplanes; i++) 20362306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, frame->payload[i]); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void fimc_buf_queue(struct vb2_buffer *vb) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 21162306a36Sopenharmony_ci struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 21262306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const struct vb2_ops fimc_qops = { 21662306a36Sopenharmony_ci .queue_setup = fimc_queue_setup, 21762306a36Sopenharmony_ci .buf_prepare = fimc_buf_prepare, 21862306a36Sopenharmony_ci .buf_queue = fimc_buf_queue, 21962306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 22062306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 22162306a36Sopenharmony_ci .stop_streaming = stop_streaming, 22262306a36Sopenharmony_ci .start_streaming = start_streaming, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * V4L2 ioctl handlers 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic int fimc_m2m_querycap(struct file *file, void *fh, 22962306a36Sopenharmony_ci struct v4l2_capability *cap) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct fimc_dev *fimc = video_drvdata(file); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci __fimc_vidioc_querycap(&fimc->pdev->dev, cap); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int fimc_m2m_enum_fmt(struct file *file, void *priv, 23862306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct fimc_fmt *fmt; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), 24362306a36Sopenharmony_ci f->index); 24462306a36Sopenharmony_ci if (!fmt) 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, 25262306a36Sopenharmony_ci struct v4l2_format *f) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(fh); 25562306a36Sopenharmony_ci struct fimc_frame *frame = ctx_get_frame(ctx, f->type); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (IS_ERR(frame)) 25862306a36Sopenharmony_ci return PTR_ERR(frame); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci __fimc_get_format(frame, f); 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 26762306a36Sopenharmony_ci const struct fimc_variant *variant = fimc->variant; 26862306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 26962306a36Sopenharmony_ci struct fimc_fmt *fmt; 27062306a36Sopenharmony_ci u32 max_w, mod_x, mod_y; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!IS_M2M(f->type)) 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci fmt = fimc_find_format(&pix->pixelformat, NULL, 27662306a36Sopenharmony_ci get_m2m_fmt_flags(f->type), 0); 27762306a36Sopenharmony_ci if (WARN(fmt == NULL, "Pixel format lookup failed")) 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (pix->field == V4L2_FIELD_ANY) 28162306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 28262306a36Sopenharmony_ci else if (pix->field != V4L2_FIELD_NONE) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 28662306a36Sopenharmony_ci max_w = variant->pix_limit->scaler_dis_w; 28762306a36Sopenharmony_ci mod_x = ffs(variant->min_inp_pixsize) - 1; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci max_w = variant->pix_limit->out_rot_dis_w; 29062306a36Sopenharmony_ci mod_x = ffs(variant->min_out_pixsize) - 1; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (tiled_fmt(fmt)) { 29462306a36Sopenharmony_ci mod_x = 6; /* 64 x 32 pixels tile */ 29562306a36Sopenharmony_ci mod_y = 5; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci if (variant->min_vsize_align == 1) 29862306a36Sopenharmony_ci mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci mod_y = ffs(variant->min_vsize_align) - 1; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci v4l_bound_align_image(&pix->width, 16, max_w, mod_x, 30462306a36Sopenharmony_ci &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, 31162306a36Sopenharmony_ci struct v4l2_format *f) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(fh); 31462306a36Sopenharmony_ci return fimc_try_fmt_mplane(ctx, f); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt, 31862306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (i = 0; i < fmt->memplanes; i++) { 32362306a36Sopenharmony_ci frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline; 32462306a36Sopenharmony_ci frame->payload[i] = pixm->plane_fmt[i].sizeimage; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci frame->f_width = pixm->width; 32862306a36Sopenharmony_ci frame->f_height = pixm->height; 32962306a36Sopenharmony_ci frame->o_width = pixm->width; 33062306a36Sopenharmony_ci frame->o_height = pixm->height; 33162306a36Sopenharmony_ci frame->width = pixm->width; 33262306a36Sopenharmony_ci frame->height = pixm->height; 33362306a36Sopenharmony_ci frame->offs_h = 0; 33462306a36Sopenharmony_ci frame->offs_v = 0; 33562306a36Sopenharmony_ci frame->fmt = fmt; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, 33962306a36Sopenharmony_ci struct v4l2_format *f) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(fh); 34262306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 34362306a36Sopenharmony_ci struct fimc_fmt *fmt; 34462306a36Sopenharmony_ci struct vb2_queue *vq; 34562306a36Sopenharmony_ci struct fimc_frame *frame; 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = fimc_try_fmt_mplane(ctx, f); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 35562306a36Sopenharmony_ci v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type); 35662306a36Sopenharmony_ci return -EBUSY; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 36062306a36Sopenharmony_ci frame = &ctx->s_frame; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci frame = &ctx->d_frame; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL, 36562306a36Sopenharmony_ci get_m2m_fmt_flags(f->type), 0); 36662306a36Sopenharmony_ci if (!fmt) 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci __set_frame_format(frame, fmt, &f->fmt.pix_mp); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Update RGB Alpha control state and value range */ 37262306a36Sopenharmony_ci fimc_alpha_ctrl_update(ctx); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int fimc_m2m_g_selection(struct file *file, void *fh, 37862306a36Sopenharmony_ci struct v4l2_selection *s) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(fh); 38162306a36Sopenharmony_ci struct fimc_frame *frame; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci frame = ctx_get_frame(ctx, s->type); 38462306a36Sopenharmony_ci if (IS_ERR(frame)) 38562306a36Sopenharmony_ci return PTR_ERR(frame); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci switch (s->target) { 38862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 38962306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 39062306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 39162306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 39562306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 39662306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 39762306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 39862306a36Sopenharmony_ci return -EINVAL; 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci return -EINVAL; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci switch (s->target) { 40562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 40662306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 40762306a36Sopenharmony_ci s->r.left = frame->offs_h; 40862306a36Sopenharmony_ci s->r.top = frame->offs_v; 40962306a36Sopenharmony_ci s->r.width = frame->width; 41062306a36Sopenharmony_ci s->r.height = frame->height; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 41362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 41462306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 41562306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 41662306a36Sopenharmony_ci s->r.left = 0; 41762306a36Sopenharmony_ci s->r.top = 0; 41862306a36Sopenharmony_ci s->r.width = frame->o_width; 41962306a36Sopenharmony_ci s->r.height = frame->o_height; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci default: 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int fimc_m2m_try_selection(struct fimc_ctx *ctx, 42862306a36Sopenharmony_ci struct v4l2_selection *s) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 43162306a36Sopenharmony_ci struct fimc_frame *f; 43262306a36Sopenharmony_ci u32 min_size, halign, depth = 0; 43362306a36Sopenharmony_ci int i; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (s->r.top < 0 || s->r.left < 0) { 43662306a36Sopenharmony_ci v4l2_err(&fimc->m2m.vfd, 43762306a36Sopenharmony_ci "doesn't support negative values for top & left\n"); 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 44162306a36Sopenharmony_ci f = &ctx->d_frame; 44262306a36Sopenharmony_ci if (s->target != V4L2_SEL_TGT_COMPOSE) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 44562306a36Sopenharmony_ci f = &ctx->s_frame; 44662306a36Sopenharmony_ci if (s->target != V4L2_SEL_TGT_CROP) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci return -EINVAL; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci min_size = (f == &ctx->s_frame) ? 45362306a36Sopenharmony_ci fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Get pixel alignment constraints. */ 45662306a36Sopenharmony_ci if (fimc->variant->min_vsize_align == 1) 45762306a36Sopenharmony_ci halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; 45862306a36Sopenharmony_ci else 45962306a36Sopenharmony_ci halign = ffs(fimc->variant->min_vsize_align) - 1; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci for (i = 0; i < f->fmt->memplanes; i++) 46262306a36Sopenharmony_ci depth += f->fmt->depth[i]; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci v4l_bound_align_image(&s->r.width, min_size, f->o_width, 46562306a36Sopenharmony_ci ffs(min_size) - 1, 46662306a36Sopenharmony_ci &s->r.height, min_size, f->o_height, 46762306a36Sopenharmony_ci halign, 64/(ALIGN(depth, 8))); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* adjust left/top if cropping rectangle is out of bounds */ 47062306a36Sopenharmony_ci if (s->r.left + s->r.width > f->o_width) 47162306a36Sopenharmony_ci s->r.left = f->o_width - s->r.width; 47262306a36Sopenharmony_ci if (s->r.top + s->r.height > f->o_height) 47362306a36Sopenharmony_ci s->r.top = f->o_height - s->r.height; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci s->r.left = round_down(s->r.left, min_size); 47662306a36Sopenharmony_ci s->r.top = round_down(s->r.top, fimc->variant->hor_offs_align); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", 47962306a36Sopenharmony_ci s->r.left, s->r.top, s->r.width, s->r.height, 48062306a36Sopenharmony_ci f->f_width, f->f_height); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int fimc_m2m_s_selection(struct file *file, void *fh, 48662306a36Sopenharmony_ci struct v4l2_selection *s) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(fh); 48962306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 49062306a36Sopenharmony_ci struct fimc_frame *f; 49162306a36Sopenharmony_ci int ret; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci ret = fimc_m2m_try_selection(ctx, s); 49462306a36Sopenharmony_ci if (ret) 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci f = (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 49862306a36Sopenharmony_ci &ctx->s_frame : &ctx->d_frame; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Check to see if scaling ratio is within supported range */ 50162306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 50262306a36Sopenharmony_ci ret = fimc_check_scaler_ratio(ctx, s->r.width, 50362306a36Sopenharmony_ci s->r.height, ctx->d_frame.width, 50462306a36Sopenharmony_ci ctx->d_frame.height, ctx->rotation); 50562306a36Sopenharmony_ci } else { 50662306a36Sopenharmony_ci ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, 50762306a36Sopenharmony_ci ctx->s_frame.height, s->r.width, 50862306a36Sopenharmony_ci s->r.height, ctx->rotation); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci if (ret) { 51162306a36Sopenharmony_ci v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); 51262306a36Sopenharmony_ci return -EINVAL; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci f->offs_h = s->r.left; 51662306a36Sopenharmony_ci f->offs_v = s->r.top; 51762306a36Sopenharmony_ci f->width = s->r.width; 51862306a36Sopenharmony_ci f->height = s->r.height; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci fimc_ctx_state_set(FIMC_PARAMS, ctx); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { 52662306a36Sopenharmony_ci .vidioc_querycap = fimc_m2m_querycap, 52762306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, 52862306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, 52962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, 53062306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, 53162306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, 53262306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, 53362306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, 53462306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, 53562306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 53662306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 53762306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 53862306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 53962306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 54062306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 54162306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 54262306a36Sopenharmony_ci .vidioc_g_selection = fimc_m2m_g_selection, 54362306a36Sopenharmony_ci .vidioc_s_selection = fimc_m2m_s_selection, 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci}; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 54862306a36Sopenharmony_ci struct vb2_queue *dst_vq) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct fimc_ctx *ctx = priv; 55162306a36Sopenharmony_ci int ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 55462306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 55562306a36Sopenharmony_ci src_vq->drv_priv = ctx; 55662306a36Sopenharmony_ci src_vq->ops = &fimc_qops; 55762306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 55862306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 55962306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 56062306a36Sopenharmony_ci src_vq->lock = &ctx->fimc_dev->lock; 56162306a36Sopenharmony_ci src_vq->dev = &ctx->fimc_dev->pdev->dev; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 56862306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 56962306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 57062306a36Sopenharmony_ci dst_vq->ops = &fimc_qops; 57162306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 57262306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 57362306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 57462306a36Sopenharmony_ci dst_vq->lock = &ctx->fimc_dev->lock; 57562306a36Sopenharmony_ci dst_vq->dev = &ctx->fimc_dev->pdev->dev; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int fimc_m2m_set_default_format(struct fimc_ctx *ctx) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct v4l2_pix_format_mplane pixm = { 58362306a36Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_RGB32, 58462306a36Sopenharmony_ci .width = 800, 58562306a36Sopenharmony_ci .height = 600, 58662306a36Sopenharmony_ci .plane_fmt[0] = { 58762306a36Sopenharmony_ci .bytesperline = 800 * 4, 58862306a36Sopenharmony_ci .sizeimage = 800 * 4 * 600, 58962306a36Sopenharmony_ci }, 59062306a36Sopenharmony_ci }; 59162306a36Sopenharmony_ci struct fimc_fmt *fmt; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0); 59462306a36Sopenharmony_ci if (!fmt) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci __set_frame_format(&ctx->s_frame, fmt, &pixm); 59862306a36Sopenharmony_ci __set_frame_format(&ctx->d_frame, fmt, &pixm); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int fimc_m2m_open(struct file *file) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct fimc_dev *fimc = video_drvdata(file); 60662306a36Sopenharmony_ci struct fimc_ctx *ctx; 60762306a36Sopenharmony_ci int ret = -EBUSY; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (mutex_lock_interruptible(&fimc->lock)) 61262306a36Sopenharmony_ci return -ERESTARTSYS; 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Don't allow simultaneous open() of the mem-to-mem and the 61562306a36Sopenharmony_ci * capture video node that belong to same FIMC IP instance. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci if (test_bit(ST_CAPT_BUSY, &fimc->state)) 61862306a36Sopenharmony_ci goto unlock; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 62162306a36Sopenharmony_ci if (!ctx) { 62262306a36Sopenharmony_ci ret = -ENOMEM; 62362306a36Sopenharmony_ci goto unlock; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd); 62662306a36Sopenharmony_ci ctx->fimc_dev = fimc; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Default color format */ 62962306a36Sopenharmony_ci ctx->s_frame.fmt = fimc_get_format(0); 63062306a36Sopenharmony_ci ctx->d_frame.fmt = fimc_get_format(0); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = fimc_ctrls_create(ctx); 63362306a36Sopenharmony_ci if (ret) 63462306a36Sopenharmony_ci goto error_fh; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Use separate control handler per file handle */ 63762306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrls.handler; 63862306a36Sopenharmony_ci file->private_data = &ctx->fh; 63962306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Setup the device context for memory-to-memory mode */ 64262306a36Sopenharmony_ci ctx->state = FIMC_CTX_M2M; 64362306a36Sopenharmony_ci ctx->flags = 0; 64462306a36Sopenharmony_ci ctx->in_path = FIMC_IO_DMA; 64562306a36Sopenharmony_ci ctx->out_path = FIMC_IO_DMA; 64662306a36Sopenharmony_ci ctx->scaler.enabled = 1; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); 64962306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 65062306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 65162306a36Sopenharmony_ci goto error_c; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (fimc->m2m.refcnt++ == 0) 65562306a36Sopenharmony_ci set_bit(ST_M2M_RUN, &fimc->state); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = fimc_m2m_set_default_format(ctx); 65862306a36Sopenharmony_ci if (ret < 0) 65962306a36Sopenharmony_ci goto error_m2m_ctx; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cierror_m2m_ctx: 66562306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 66662306a36Sopenharmony_cierror_c: 66762306a36Sopenharmony_ci fimc_ctrls_delete(ctx); 66862306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 66962306a36Sopenharmony_cierror_fh: 67062306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 67162306a36Sopenharmony_ci kfree(ctx); 67262306a36Sopenharmony_ciunlock: 67362306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int fimc_m2m_release(struct file *file) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct fimc_ctx *ctx = fh_to_ctx(file->private_data); 68062306a36Sopenharmony_ci struct fimc_dev *fimc = ctx->fimc_dev; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci dbg("pid: %d, state: 0x%lx, refcnt= %d", 68362306a36Sopenharmony_ci task_pid_nr(current), fimc->state, fimc->m2m.refcnt); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci mutex_lock(&fimc->lock); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 68862306a36Sopenharmony_ci fimc_ctrls_delete(ctx); 68962306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 69062306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (--fimc->m2m.refcnt <= 0) 69362306a36Sopenharmony_ci clear_bit(ST_M2M_RUN, &fimc->state); 69462306a36Sopenharmony_ci kfree(ctx); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic const struct v4l2_file_operations fimc_m2m_fops = { 70162306a36Sopenharmony_ci .owner = THIS_MODULE, 70262306a36Sopenharmony_ci .open = fimc_m2m_open, 70362306a36Sopenharmony_ci .release = fimc_m2m_release, 70462306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 70562306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 70662306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 70762306a36Sopenharmony_ci}; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic const struct v4l2_m2m_ops m2m_ops = { 71062306a36Sopenharmony_ci .device_run = fimc_device_run, 71162306a36Sopenharmony_ci .job_abort = fimc_job_abort, 71262306a36Sopenharmony_ci}; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciint fimc_register_m2m_device(struct fimc_dev *fimc, 71562306a36Sopenharmony_ci struct v4l2_device *v4l2_dev) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct video_device *vfd = &fimc->m2m.vfd; 71862306a36Sopenharmony_ci int ret; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci fimc->v4l2_dev = v4l2_dev; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci memset(vfd, 0, sizeof(*vfd)); 72362306a36Sopenharmony_ci vfd->fops = &fimc_m2m_fops; 72462306a36Sopenharmony_ci vfd->ioctl_ops = &fimc_m2m_ioctl_ops; 72562306a36Sopenharmony_ci vfd->v4l2_dev = v4l2_dev; 72662306a36Sopenharmony_ci vfd->minor = -1; 72762306a36Sopenharmony_ci vfd->release = video_device_release_empty; 72862306a36Sopenharmony_ci vfd->lock = &fimc->lock; 72962306a36Sopenharmony_ci vfd->vfl_dir = VFL_DIR_M2M; 73062306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; 73162306a36Sopenharmony_ci set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); 73462306a36Sopenharmony_ci video_set_drvdata(vfd, fimc); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); 73762306a36Sopenharmony_ci if (IS_ERR(fimc->m2m.m2m_dev)) { 73862306a36Sopenharmony_ci v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); 73962306a36Sopenharmony_ci return PTR_ERR(fimc->m2m.m2m_dev); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci ret = media_entity_pads_init(&vfd->entity, 0, NULL); 74362306a36Sopenharmony_ci if (ret) 74462306a36Sopenharmony_ci goto err_me; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); 74762306a36Sopenharmony_ci if (ret) 74862306a36Sopenharmony_ci goto err_vd; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", 75162306a36Sopenharmony_ci vfd->name, video_device_node_name(vfd)); 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cierr_vd: 75562306a36Sopenharmony_ci media_entity_cleanup(&vfd->entity); 75662306a36Sopenharmony_cierr_me: 75762306a36Sopenharmony_ci v4l2_m2m_release(fimc->m2m.m2m_dev); 75862306a36Sopenharmony_ci return ret; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_civoid fimc_unregister_m2m_device(struct fimc_dev *fimc) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci if (!fimc) 76462306a36Sopenharmony_ci return; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (fimc->m2m.m2m_dev) 76762306a36Sopenharmony_ci v4l2_m2m_release(fimc->m2m.m2m_dev); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (video_is_registered(&fimc->m2m.vfd)) { 77062306a36Sopenharmony_ci video_unregister_device(&fimc->m2m.vfd); 77162306a36Sopenharmony_ci media_entity_cleanup(&fimc->m2m.vfd.entity); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci} 774