1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun * Sylwester Nawrocki <s.nawrocki@samsung.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/types.h>
12*4882a593Smuzhiyun #include <linux/errno.h>
13*4882a593Smuzhiyun #include <linux/bug.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/pm_runtime.h>
18*4882a593Smuzhiyun #include <linux/list.h>
19*4882a593Smuzhiyun #include <linux/io.h>
20*4882a593Smuzhiyun #include <linux/slab.h>
21*4882a593Smuzhiyun #include <linux/clk.h>
22*4882a593Smuzhiyun #include <media/v4l2-ioctl.h>
23*4882a593Smuzhiyun #include <media/videobuf2-v4l2.h>
24*4882a593Smuzhiyun #include <media/videobuf2-dma-contig.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include "common.h"
27*4882a593Smuzhiyun #include "fimc-core.h"
28*4882a593Smuzhiyun #include "fimc-reg.h"
29*4882a593Smuzhiyun #include "media-dev.h"
30*4882a593Smuzhiyun
get_m2m_fmt_flags(unsigned int stream_type)31*4882a593Smuzhiyun static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
34*4882a593Smuzhiyun return FMT_FLAGS_M2M_IN;
35*4882a593Smuzhiyun else
36*4882a593Smuzhiyun return FMT_FLAGS_M2M_OUT;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun
fimc_m2m_job_finish(struct fimc_ctx * ctx,int vb_state)39*4882a593Smuzhiyun void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct vb2_v4l2_buffer *src_vb, *dst_vb;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun if (!ctx || !ctx->fh.m2m_ctx)
44*4882a593Smuzhiyun return;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
47*4882a593Smuzhiyun dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (src_vb)
50*4882a593Smuzhiyun v4l2_m2m_buf_done(src_vb, vb_state);
51*4882a593Smuzhiyun if (dst_vb)
52*4882a593Smuzhiyun v4l2_m2m_buf_done(dst_vb, vb_state);
53*4882a593Smuzhiyun if (src_vb && dst_vb)
54*4882a593Smuzhiyun v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
55*4882a593Smuzhiyun ctx->fh.m2m_ctx);
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* Complete the transaction which has been scheduled for execution. */
fimc_m2m_shutdown(struct fimc_ctx * ctx)59*4882a593Smuzhiyun static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (!fimc_m2m_pending(fimc))
64*4882a593Smuzhiyun return;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun wait_event_timeout(fimc->irq_queue,
69*4882a593Smuzhiyun !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
70*4882a593Smuzhiyun FIMC_SHUTDOWN_TIMEOUT);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
start_streaming(struct vb2_queue * q,unsigned int count)73*4882a593Smuzhiyun static int start_streaming(struct vb2_queue *q, unsigned int count)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct fimc_ctx *ctx = q->drv_priv;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun return pm_runtime_resume_and_get(&ctx->fimc_dev->pdev->dev);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
stop_streaming(struct vb2_queue * q)80*4882a593Smuzhiyun static void stop_streaming(struct vb2_queue *q)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct fimc_ctx *ctx = q->drv_priv;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun fimc_m2m_shutdown(ctx);
85*4882a593Smuzhiyun fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
86*4882a593Smuzhiyun pm_runtime_put(&ctx->fimc_dev->pdev->dev);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
fimc_device_run(void * priv)89*4882a593Smuzhiyun static void fimc_device_run(void *priv)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct vb2_v4l2_buffer *src_vb, *dst_vb;
92*4882a593Smuzhiyun struct fimc_ctx *ctx = priv;
93*4882a593Smuzhiyun struct fimc_frame *sf, *df;
94*4882a593Smuzhiyun struct fimc_dev *fimc;
95*4882a593Smuzhiyun unsigned long flags;
96*4882a593Smuzhiyun int ret;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (WARN(!ctx, "Null context\n"))
99*4882a593Smuzhiyun return;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun fimc = ctx->fimc_dev;
102*4882a593Smuzhiyun spin_lock_irqsave(&fimc->slock, flags);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun set_bit(ST_M2M_PEND, &fimc->state);
105*4882a593Smuzhiyun sf = &ctx->s_frame;
106*4882a593Smuzhiyun df = &ctx->d_frame;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (ctx->state & FIMC_PARAMS) {
109*4882a593Smuzhiyun /* Prepare the DMA offsets for scaler */
110*4882a593Smuzhiyun fimc_prepare_dma_offset(ctx, sf);
111*4882a593Smuzhiyun fimc_prepare_dma_offset(ctx, df);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
115*4882a593Smuzhiyun ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->paddr);
116*4882a593Smuzhiyun if (ret)
117*4882a593Smuzhiyun goto dma_unlock;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
120*4882a593Smuzhiyun ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->paddr);
121*4882a593Smuzhiyun if (ret)
122*4882a593Smuzhiyun goto dma_unlock;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
125*4882a593Smuzhiyun dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
126*4882a593Smuzhiyun dst_vb->flags |=
127*4882a593Smuzhiyun src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /* Reconfigure hardware if the context has changed. */
130*4882a593Smuzhiyun if (fimc->m2m.ctx != ctx) {
131*4882a593Smuzhiyun ctx->state |= FIMC_PARAMS;
132*4882a593Smuzhiyun fimc->m2m.ctx = ctx;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun if (ctx->state & FIMC_PARAMS) {
136*4882a593Smuzhiyun fimc_set_yuv_order(ctx);
137*4882a593Smuzhiyun fimc_hw_set_input_path(ctx);
138*4882a593Smuzhiyun fimc_hw_set_in_dma(ctx);
139*4882a593Smuzhiyun ret = fimc_set_scaler_info(ctx);
140*4882a593Smuzhiyun if (ret)
141*4882a593Smuzhiyun goto dma_unlock;
142*4882a593Smuzhiyun fimc_hw_set_prescaler(ctx);
143*4882a593Smuzhiyun fimc_hw_set_mainscaler(ctx);
144*4882a593Smuzhiyun fimc_hw_set_target_format(ctx);
145*4882a593Smuzhiyun fimc_hw_set_rotation(ctx);
146*4882a593Smuzhiyun fimc_hw_set_effect(ctx);
147*4882a593Smuzhiyun fimc_hw_set_out_dma(ctx);
148*4882a593Smuzhiyun if (fimc->drv_data->alpha_color)
149*4882a593Smuzhiyun fimc_hw_set_rgb_alpha(ctx);
150*4882a593Smuzhiyun fimc_hw_set_output_path(ctx);
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun fimc_hw_set_input_addr(fimc, &sf->paddr);
153*4882a593Smuzhiyun fimc_hw_set_output_addr(fimc, &df->paddr, -1);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun fimc_activate_capture(ctx);
156*4882a593Smuzhiyun ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
157*4882a593Smuzhiyun fimc_hw_activate_input_dma(fimc, true);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun dma_unlock:
160*4882a593Smuzhiyun spin_unlock_irqrestore(&fimc->slock, flags);
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
fimc_job_abort(void * priv)163*4882a593Smuzhiyun static void fimc_job_abort(void *priv)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun fimc_m2m_shutdown(priv);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
fimc_queue_setup(struct vb2_queue * vq,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])168*4882a593Smuzhiyun static int fimc_queue_setup(struct vb2_queue *vq,
169*4882a593Smuzhiyun unsigned int *num_buffers, unsigned int *num_planes,
170*4882a593Smuzhiyun unsigned int sizes[], struct device *alloc_devs[])
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
173*4882a593Smuzhiyun struct fimc_frame *f;
174*4882a593Smuzhiyun int i;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun f = ctx_get_frame(ctx, vq->type);
177*4882a593Smuzhiyun if (IS_ERR(f))
178*4882a593Smuzhiyun return PTR_ERR(f);
179*4882a593Smuzhiyun /*
180*4882a593Smuzhiyun * Return number of non-contiguous planes (plane buffers)
181*4882a593Smuzhiyun * depending on the configured color format.
182*4882a593Smuzhiyun */
183*4882a593Smuzhiyun if (!f->fmt)
184*4882a593Smuzhiyun return -EINVAL;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun *num_planes = f->fmt->memplanes;
187*4882a593Smuzhiyun for (i = 0; i < f->fmt->memplanes; i++)
188*4882a593Smuzhiyun sizes[i] = f->payload[i];
189*4882a593Smuzhiyun return 0;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
fimc_buf_prepare(struct vb2_buffer * vb)192*4882a593Smuzhiyun static int fimc_buf_prepare(struct vb2_buffer *vb)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
195*4882a593Smuzhiyun struct fimc_frame *frame;
196*4882a593Smuzhiyun int i;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun frame = ctx_get_frame(ctx, vb->vb2_queue->type);
199*4882a593Smuzhiyun if (IS_ERR(frame))
200*4882a593Smuzhiyun return PTR_ERR(frame);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun for (i = 0; i < frame->fmt->memplanes; i++)
203*4882a593Smuzhiyun vb2_set_plane_payload(vb, i, frame->payload[i]);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun return 0;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
fimc_buf_queue(struct vb2_buffer * vb)208*4882a593Smuzhiyun static void fimc_buf_queue(struct vb2_buffer *vb)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
211*4882a593Smuzhiyun struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
212*4882a593Smuzhiyun v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun static const struct vb2_ops fimc_qops = {
216*4882a593Smuzhiyun .queue_setup = fimc_queue_setup,
217*4882a593Smuzhiyun .buf_prepare = fimc_buf_prepare,
218*4882a593Smuzhiyun .buf_queue = fimc_buf_queue,
219*4882a593Smuzhiyun .wait_prepare = vb2_ops_wait_prepare,
220*4882a593Smuzhiyun .wait_finish = vb2_ops_wait_finish,
221*4882a593Smuzhiyun .stop_streaming = stop_streaming,
222*4882a593Smuzhiyun .start_streaming = start_streaming,
223*4882a593Smuzhiyun };
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /*
226*4882a593Smuzhiyun * V4L2 ioctl handlers
227*4882a593Smuzhiyun */
fimc_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)228*4882a593Smuzhiyun static int fimc_m2m_querycap(struct file *file, void *fh,
229*4882a593Smuzhiyun struct v4l2_capability *cap)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun struct fimc_dev *fimc = video_drvdata(file);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun __fimc_vidioc_querycap(&fimc->pdev->dev, cap);
234*4882a593Smuzhiyun return 0;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
fimc_m2m_enum_fmt(struct file * file,void * priv,struct v4l2_fmtdesc * f)237*4882a593Smuzhiyun static int fimc_m2m_enum_fmt(struct file *file, void *priv,
238*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun struct fimc_fmt *fmt;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
243*4882a593Smuzhiyun f->index);
244*4882a593Smuzhiyun if (!fmt)
245*4882a593Smuzhiyun return -EINVAL;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun f->pixelformat = fmt->fourcc;
248*4882a593Smuzhiyun return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
fimc_m2m_g_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)251*4882a593Smuzhiyun static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
252*4882a593Smuzhiyun struct v4l2_format *f)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(fh);
255*4882a593Smuzhiyun struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (IS_ERR(frame))
258*4882a593Smuzhiyun return PTR_ERR(frame);
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun __fimc_get_format(frame, f);
261*4882a593Smuzhiyun return 0;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
fimc_try_fmt_mplane(struct fimc_ctx * ctx,struct v4l2_format * f)264*4882a593Smuzhiyun static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
267*4882a593Smuzhiyun const struct fimc_variant *variant = fimc->variant;
268*4882a593Smuzhiyun struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
269*4882a593Smuzhiyun struct fimc_fmt *fmt;
270*4882a593Smuzhiyun u32 max_w, mod_x, mod_y;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun if (!IS_M2M(f->type))
273*4882a593Smuzhiyun return -EINVAL;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun fmt = fimc_find_format(&pix->pixelformat, NULL,
276*4882a593Smuzhiyun get_m2m_fmt_flags(f->type), 0);
277*4882a593Smuzhiyun if (WARN(fmt == NULL, "Pixel format lookup failed"))
278*4882a593Smuzhiyun return -EINVAL;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun if (pix->field == V4L2_FIELD_ANY)
281*4882a593Smuzhiyun pix->field = V4L2_FIELD_NONE;
282*4882a593Smuzhiyun else if (pix->field != V4L2_FIELD_NONE)
283*4882a593Smuzhiyun return -EINVAL;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
286*4882a593Smuzhiyun max_w = variant->pix_limit->scaler_dis_w;
287*4882a593Smuzhiyun mod_x = ffs(variant->min_inp_pixsize) - 1;
288*4882a593Smuzhiyun } else {
289*4882a593Smuzhiyun max_w = variant->pix_limit->out_rot_dis_w;
290*4882a593Smuzhiyun mod_x = ffs(variant->min_out_pixsize) - 1;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun if (tiled_fmt(fmt)) {
294*4882a593Smuzhiyun mod_x = 6; /* 64 x 32 pixels tile */
295*4882a593Smuzhiyun mod_y = 5;
296*4882a593Smuzhiyun } else {
297*4882a593Smuzhiyun if (variant->min_vsize_align == 1)
298*4882a593Smuzhiyun mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
299*4882a593Smuzhiyun else
300*4882a593Smuzhiyun mod_y = ffs(variant->min_vsize_align) - 1;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
304*4882a593Smuzhiyun &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
307*4882a593Smuzhiyun return 0;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
fimc_m2m_try_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)310*4882a593Smuzhiyun static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
311*4882a593Smuzhiyun struct v4l2_format *f)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(fh);
314*4882a593Smuzhiyun return fimc_try_fmt_mplane(ctx, f);
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
__set_frame_format(struct fimc_frame * frame,struct fimc_fmt * fmt,struct v4l2_pix_format_mplane * pixm)317*4882a593Smuzhiyun static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt,
318*4882a593Smuzhiyun struct v4l2_pix_format_mplane *pixm)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun int i;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun for (i = 0; i < fmt->memplanes; i++) {
323*4882a593Smuzhiyun frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;
324*4882a593Smuzhiyun frame->payload[i] = pixm->plane_fmt[i].sizeimage;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun frame->f_width = pixm->width;
328*4882a593Smuzhiyun frame->f_height = pixm->height;
329*4882a593Smuzhiyun frame->o_width = pixm->width;
330*4882a593Smuzhiyun frame->o_height = pixm->height;
331*4882a593Smuzhiyun frame->width = pixm->width;
332*4882a593Smuzhiyun frame->height = pixm->height;
333*4882a593Smuzhiyun frame->offs_h = 0;
334*4882a593Smuzhiyun frame->offs_v = 0;
335*4882a593Smuzhiyun frame->fmt = fmt;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun
fimc_m2m_s_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)338*4882a593Smuzhiyun static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
339*4882a593Smuzhiyun struct v4l2_format *f)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(fh);
342*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
343*4882a593Smuzhiyun struct fimc_fmt *fmt;
344*4882a593Smuzhiyun struct vb2_queue *vq;
345*4882a593Smuzhiyun struct fimc_frame *frame;
346*4882a593Smuzhiyun int ret;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun ret = fimc_try_fmt_mplane(ctx, f);
349*4882a593Smuzhiyun if (ret)
350*4882a593Smuzhiyun return ret;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun if (vb2_is_busy(vq)) {
355*4882a593Smuzhiyun v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
356*4882a593Smuzhiyun return -EBUSY;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
360*4882a593Smuzhiyun frame = &ctx->s_frame;
361*4882a593Smuzhiyun else
362*4882a593Smuzhiyun frame = &ctx->d_frame;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL,
365*4882a593Smuzhiyun get_m2m_fmt_flags(f->type), 0);
366*4882a593Smuzhiyun if (!fmt)
367*4882a593Smuzhiyun return -EINVAL;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun __set_frame_format(frame, fmt, &f->fmt.pix_mp);
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* Update RGB Alpha control state and value range */
372*4882a593Smuzhiyun fimc_alpha_ctrl_update(ctx);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun return 0;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
fimc_m2m_g_selection(struct file * file,void * fh,struct v4l2_selection * s)377*4882a593Smuzhiyun static int fimc_m2m_g_selection(struct file *file, void *fh,
378*4882a593Smuzhiyun struct v4l2_selection *s)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(fh);
381*4882a593Smuzhiyun struct fimc_frame *frame;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun frame = ctx_get_frame(ctx, s->type);
384*4882a593Smuzhiyun if (IS_ERR(frame))
385*4882a593Smuzhiyun return PTR_ERR(frame);
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun switch (s->target) {
388*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP:
389*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_DEFAULT:
390*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_BOUNDS:
391*4882a593Smuzhiyun if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
392*4882a593Smuzhiyun return -EINVAL;
393*4882a593Smuzhiyun break;
394*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE:
395*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_DEFAULT:
396*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_BOUNDS:
397*4882a593Smuzhiyun if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
398*4882a593Smuzhiyun return -EINVAL;
399*4882a593Smuzhiyun break;
400*4882a593Smuzhiyun default:
401*4882a593Smuzhiyun return -EINVAL;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun switch (s->target) {
405*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP:
406*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE:
407*4882a593Smuzhiyun s->r.left = frame->offs_h;
408*4882a593Smuzhiyun s->r.top = frame->offs_v;
409*4882a593Smuzhiyun s->r.width = frame->width;
410*4882a593Smuzhiyun s->r.height = frame->height;
411*4882a593Smuzhiyun break;
412*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_DEFAULT:
413*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_BOUNDS:
414*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_DEFAULT:
415*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_BOUNDS:
416*4882a593Smuzhiyun s->r.left = 0;
417*4882a593Smuzhiyun s->r.top = 0;
418*4882a593Smuzhiyun s->r.width = frame->o_width;
419*4882a593Smuzhiyun s->r.height = frame->o_height;
420*4882a593Smuzhiyun break;
421*4882a593Smuzhiyun default:
422*4882a593Smuzhiyun return -EINVAL;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun return 0;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
fimc_m2m_try_selection(struct fimc_ctx * ctx,struct v4l2_selection * s)427*4882a593Smuzhiyun static int fimc_m2m_try_selection(struct fimc_ctx *ctx,
428*4882a593Smuzhiyun struct v4l2_selection *s)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
431*4882a593Smuzhiyun struct fimc_frame *f;
432*4882a593Smuzhiyun u32 min_size, halign, depth = 0;
433*4882a593Smuzhiyun int i;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun if (s->r.top < 0 || s->r.left < 0) {
436*4882a593Smuzhiyun v4l2_err(&fimc->m2m.vfd,
437*4882a593Smuzhiyun "doesn't support negative values for top & left\n");
438*4882a593Smuzhiyun return -EINVAL;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
441*4882a593Smuzhiyun f = &ctx->d_frame;
442*4882a593Smuzhiyun if (s->target != V4L2_SEL_TGT_COMPOSE)
443*4882a593Smuzhiyun return -EINVAL;
444*4882a593Smuzhiyun } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
445*4882a593Smuzhiyun f = &ctx->s_frame;
446*4882a593Smuzhiyun if (s->target != V4L2_SEL_TGT_CROP)
447*4882a593Smuzhiyun return -EINVAL;
448*4882a593Smuzhiyun } else {
449*4882a593Smuzhiyun return -EINVAL;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun min_size = (f == &ctx->s_frame) ?
453*4882a593Smuzhiyun fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun /* Get pixel alignment constraints. */
456*4882a593Smuzhiyun if (fimc->variant->min_vsize_align == 1)
457*4882a593Smuzhiyun halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
458*4882a593Smuzhiyun else
459*4882a593Smuzhiyun halign = ffs(fimc->variant->min_vsize_align) - 1;
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun for (i = 0; i < f->fmt->memplanes; i++)
462*4882a593Smuzhiyun depth += f->fmt->depth[i];
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun v4l_bound_align_image(&s->r.width, min_size, f->o_width,
465*4882a593Smuzhiyun ffs(min_size) - 1,
466*4882a593Smuzhiyun &s->r.height, min_size, f->o_height,
467*4882a593Smuzhiyun halign, 64/(ALIGN(depth, 8)));
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun /* adjust left/top if cropping rectangle is out of bounds */
470*4882a593Smuzhiyun if (s->r.left + s->r.width > f->o_width)
471*4882a593Smuzhiyun s->r.left = f->o_width - s->r.width;
472*4882a593Smuzhiyun if (s->r.top + s->r.height > f->o_height)
473*4882a593Smuzhiyun s->r.top = f->o_height - s->r.height;
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun s->r.left = round_down(s->r.left, min_size);
476*4882a593Smuzhiyun s->r.top = round_down(s->r.top, fimc->variant->hor_offs_align);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
479*4882a593Smuzhiyun s->r.left, s->r.top, s->r.width, s->r.height,
480*4882a593Smuzhiyun f->f_width, f->f_height);
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun return 0;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
fimc_m2m_s_selection(struct file * file,void * fh,struct v4l2_selection * s)485*4882a593Smuzhiyun static int fimc_m2m_s_selection(struct file *file, void *fh,
486*4882a593Smuzhiyun struct v4l2_selection *s)
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(fh);
489*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
490*4882a593Smuzhiyun struct fimc_frame *f;
491*4882a593Smuzhiyun int ret;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun ret = fimc_m2m_try_selection(ctx, s);
494*4882a593Smuzhiyun if (ret)
495*4882a593Smuzhiyun return ret;
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun f = (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
498*4882a593Smuzhiyun &ctx->s_frame : &ctx->d_frame;
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun /* Check to see if scaling ratio is within supported range */
501*4882a593Smuzhiyun if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
502*4882a593Smuzhiyun ret = fimc_check_scaler_ratio(ctx, s->r.width,
503*4882a593Smuzhiyun s->r.height, ctx->d_frame.width,
504*4882a593Smuzhiyun ctx->d_frame.height, ctx->rotation);
505*4882a593Smuzhiyun } else {
506*4882a593Smuzhiyun ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
507*4882a593Smuzhiyun ctx->s_frame.height, s->r.width,
508*4882a593Smuzhiyun s->r.height, ctx->rotation);
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun if (ret) {
511*4882a593Smuzhiyun v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
512*4882a593Smuzhiyun return -EINVAL;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun f->offs_h = s->r.left;
516*4882a593Smuzhiyun f->offs_v = s->r.top;
517*4882a593Smuzhiyun f->width = s->r.width;
518*4882a593Smuzhiyun f->height = s->r.height;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun fimc_ctx_state_set(FIMC_PARAMS, ctx);
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun return 0;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
526*4882a593Smuzhiyun .vidioc_querycap = fimc_m2m_querycap,
527*4882a593Smuzhiyun .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt,
528*4882a593Smuzhiyun .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt,
529*4882a593Smuzhiyun .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
530*4882a593Smuzhiyun .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
531*4882a593Smuzhiyun .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
532*4882a593Smuzhiyun .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
533*4882a593Smuzhiyun .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
534*4882a593Smuzhiyun .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
535*4882a593Smuzhiyun .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
536*4882a593Smuzhiyun .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
537*4882a593Smuzhiyun .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
538*4882a593Smuzhiyun .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
539*4882a593Smuzhiyun .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
540*4882a593Smuzhiyun .vidioc_streamon = v4l2_m2m_ioctl_streamon,
541*4882a593Smuzhiyun .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
542*4882a593Smuzhiyun .vidioc_g_selection = fimc_m2m_g_selection,
543*4882a593Smuzhiyun .vidioc_s_selection = fimc_m2m_s_selection,
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun };
546*4882a593Smuzhiyun
queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)547*4882a593Smuzhiyun static int queue_init(void *priv, struct vb2_queue *src_vq,
548*4882a593Smuzhiyun struct vb2_queue *dst_vq)
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun struct fimc_ctx *ctx = priv;
551*4882a593Smuzhiyun int ret;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
554*4882a593Smuzhiyun src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
555*4882a593Smuzhiyun src_vq->drv_priv = ctx;
556*4882a593Smuzhiyun src_vq->ops = &fimc_qops;
557*4882a593Smuzhiyun src_vq->mem_ops = &vb2_dma_contig_memops;
558*4882a593Smuzhiyun src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
559*4882a593Smuzhiyun src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
560*4882a593Smuzhiyun src_vq->lock = &ctx->fimc_dev->lock;
561*4882a593Smuzhiyun src_vq->dev = &ctx->fimc_dev->pdev->dev;
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun ret = vb2_queue_init(src_vq);
564*4882a593Smuzhiyun if (ret)
565*4882a593Smuzhiyun return ret;
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
568*4882a593Smuzhiyun dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
569*4882a593Smuzhiyun dst_vq->drv_priv = ctx;
570*4882a593Smuzhiyun dst_vq->ops = &fimc_qops;
571*4882a593Smuzhiyun dst_vq->mem_ops = &vb2_dma_contig_memops;
572*4882a593Smuzhiyun dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
573*4882a593Smuzhiyun dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
574*4882a593Smuzhiyun dst_vq->lock = &ctx->fimc_dev->lock;
575*4882a593Smuzhiyun dst_vq->dev = &ctx->fimc_dev->pdev->dev;
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun return vb2_queue_init(dst_vq);
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun
fimc_m2m_set_default_format(struct fimc_ctx * ctx)580*4882a593Smuzhiyun static int fimc_m2m_set_default_format(struct fimc_ctx *ctx)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun struct v4l2_pix_format_mplane pixm = {
583*4882a593Smuzhiyun .pixelformat = V4L2_PIX_FMT_RGB32,
584*4882a593Smuzhiyun .width = 800,
585*4882a593Smuzhiyun .height = 600,
586*4882a593Smuzhiyun .plane_fmt[0] = {
587*4882a593Smuzhiyun .bytesperline = 800 * 4,
588*4882a593Smuzhiyun .sizeimage = 800 * 4 * 600,
589*4882a593Smuzhiyun },
590*4882a593Smuzhiyun };
591*4882a593Smuzhiyun struct fimc_fmt *fmt;
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0);
594*4882a593Smuzhiyun if (!fmt)
595*4882a593Smuzhiyun return -EINVAL;
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun __set_frame_format(&ctx->s_frame, fmt, &pixm);
598*4882a593Smuzhiyun __set_frame_format(&ctx->d_frame, fmt, &pixm);
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun return 0;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun
fimc_m2m_open(struct file * file)603*4882a593Smuzhiyun static int fimc_m2m_open(struct file *file)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun struct fimc_dev *fimc = video_drvdata(file);
606*4882a593Smuzhiyun struct fimc_ctx *ctx;
607*4882a593Smuzhiyun int ret = -EBUSY;
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state);
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun if (mutex_lock_interruptible(&fimc->lock))
612*4882a593Smuzhiyun return -ERESTARTSYS;
613*4882a593Smuzhiyun /*
614*4882a593Smuzhiyun * Don't allow simultaneous open() of the mem-to-mem and the
615*4882a593Smuzhiyun * capture video node that belong to same FIMC IP instance.
616*4882a593Smuzhiyun */
617*4882a593Smuzhiyun if (test_bit(ST_CAPT_BUSY, &fimc->state))
618*4882a593Smuzhiyun goto unlock;
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
621*4882a593Smuzhiyun if (!ctx) {
622*4882a593Smuzhiyun ret = -ENOMEM;
623*4882a593Smuzhiyun goto unlock;
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd);
626*4882a593Smuzhiyun ctx->fimc_dev = fimc;
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun /* Default color format */
629*4882a593Smuzhiyun ctx->s_frame.fmt = fimc_get_format(0);
630*4882a593Smuzhiyun ctx->d_frame.fmt = fimc_get_format(0);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun ret = fimc_ctrls_create(ctx);
633*4882a593Smuzhiyun if (ret)
634*4882a593Smuzhiyun goto error_fh;
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun /* Use separate control handler per file handle */
637*4882a593Smuzhiyun ctx->fh.ctrl_handler = &ctx->ctrls.handler;
638*4882a593Smuzhiyun file->private_data = &ctx->fh;
639*4882a593Smuzhiyun v4l2_fh_add(&ctx->fh);
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun /* Setup the device context for memory-to-memory mode */
642*4882a593Smuzhiyun ctx->state = FIMC_CTX_M2M;
643*4882a593Smuzhiyun ctx->flags = 0;
644*4882a593Smuzhiyun ctx->in_path = FIMC_IO_DMA;
645*4882a593Smuzhiyun ctx->out_path = FIMC_IO_DMA;
646*4882a593Smuzhiyun ctx->scaler.enabled = 1;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
649*4882a593Smuzhiyun if (IS_ERR(ctx->fh.m2m_ctx)) {
650*4882a593Smuzhiyun ret = PTR_ERR(ctx->fh.m2m_ctx);
651*4882a593Smuzhiyun goto error_c;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun if (fimc->m2m.refcnt++ == 0)
655*4882a593Smuzhiyun set_bit(ST_M2M_RUN, &fimc->state);
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun ret = fimc_m2m_set_default_format(ctx);
658*4882a593Smuzhiyun if (ret < 0)
659*4882a593Smuzhiyun goto error_m2m_ctx;
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun mutex_unlock(&fimc->lock);
662*4882a593Smuzhiyun return 0;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun error_m2m_ctx:
665*4882a593Smuzhiyun v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
666*4882a593Smuzhiyun error_c:
667*4882a593Smuzhiyun fimc_ctrls_delete(ctx);
668*4882a593Smuzhiyun v4l2_fh_del(&ctx->fh);
669*4882a593Smuzhiyun error_fh:
670*4882a593Smuzhiyun v4l2_fh_exit(&ctx->fh);
671*4882a593Smuzhiyun kfree(ctx);
672*4882a593Smuzhiyun unlock:
673*4882a593Smuzhiyun mutex_unlock(&fimc->lock);
674*4882a593Smuzhiyun return ret;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun
fimc_m2m_release(struct file * file)677*4882a593Smuzhiyun static int fimc_m2m_release(struct file *file)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
680*4882a593Smuzhiyun struct fimc_dev *fimc = ctx->fimc_dev;
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun dbg("pid: %d, state: 0x%lx, refcnt= %d",
683*4882a593Smuzhiyun task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun mutex_lock(&fimc->lock);
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
688*4882a593Smuzhiyun fimc_ctrls_delete(ctx);
689*4882a593Smuzhiyun v4l2_fh_del(&ctx->fh);
690*4882a593Smuzhiyun v4l2_fh_exit(&ctx->fh);
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun if (--fimc->m2m.refcnt <= 0)
693*4882a593Smuzhiyun clear_bit(ST_M2M_RUN, &fimc->state);
694*4882a593Smuzhiyun kfree(ctx);
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun mutex_unlock(&fimc->lock);
697*4882a593Smuzhiyun return 0;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun static const struct v4l2_file_operations fimc_m2m_fops = {
701*4882a593Smuzhiyun .owner = THIS_MODULE,
702*4882a593Smuzhiyun .open = fimc_m2m_open,
703*4882a593Smuzhiyun .release = fimc_m2m_release,
704*4882a593Smuzhiyun .poll = v4l2_m2m_fop_poll,
705*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
706*4882a593Smuzhiyun .mmap = v4l2_m2m_fop_mmap,
707*4882a593Smuzhiyun };
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun static const struct v4l2_m2m_ops m2m_ops = {
710*4882a593Smuzhiyun .device_run = fimc_device_run,
711*4882a593Smuzhiyun .job_abort = fimc_job_abort,
712*4882a593Smuzhiyun };
713*4882a593Smuzhiyun
fimc_register_m2m_device(struct fimc_dev * fimc,struct v4l2_device * v4l2_dev)714*4882a593Smuzhiyun int fimc_register_m2m_device(struct fimc_dev *fimc,
715*4882a593Smuzhiyun struct v4l2_device *v4l2_dev)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun struct video_device *vfd = &fimc->m2m.vfd;
718*4882a593Smuzhiyun int ret;
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun fimc->v4l2_dev = v4l2_dev;
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun memset(vfd, 0, sizeof(*vfd));
723*4882a593Smuzhiyun vfd->fops = &fimc_m2m_fops;
724*4882a593Smuzhiyun vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
725*4882a593Smuzhiyun vfd->v4l2_dev = v4l2_dev;
726*4882a593Smuzhiyun vfd->minor = -1;
727*4882a593Smuzhiyun vfd->release = video_device_release_empty;
728*4882a593Smuzhiyun vfd->lock = &fimc->lock;
729*4882a593Smuzhiyun vfd->vfl_dir = VFL_DIR_M2M;
730*4882a593Smuzhiyun vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
731*4882a593Smuzhiyun set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
734*4882a593Smuzhiyun video_set_drvdata(vfd, fimc);
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
737*4882a593Smuzhiyun if (IS_ERR(fimc->m2m.m2m_dev)) {
738*4882a593Smuzhiyun v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
739*4882a593Smuzhiyun return PTR_ERR(fimc->m2m.m2m_dev);
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun ret = media_entity_pads_init(&vfd->entity, 0, NULL);
743*4882a593Smuzhiyun if (ret)
744*4882a593Smuzhiyun goto err_me;
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
747*4882a593Smuzhiyun if (ret)
748*4882a593Smuzhiyun goto err_vd;
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
751*4882a593Smuzhiyun vfd->name, video_device_node_name(vfd));
752*4882a593Smuzhiyun return 0;
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun err_vd:
755*4882a593Smuzhiyun media_entity_cleanup(&vfd->entity);
756*4882a593Smuzhiyun err_me:
757*4882a593Smuzhiyun v4l2_m2m_release(fimc->m2m.m2m_dev);
758*4882a593Smuzhiyun return ret;
759*4882a593Smuzhiyun }
760*4882a593Smuzhiyun
fimc_unregister_m2m_device(struct fimc_dev * fimc)761*4882a593Smuzhiyun void fimc_unregister_m2m_device(struct fimc_dev *fimc)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun if (!fimc)
764*4882a593Smuzhiyun return;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun if (fimc->m2m.m2m_dev)
767*4882a593Smuzhiyun v4l2_m2m_release(fimc->m2m.m2m_dev);
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun if (video_is_registered(&fimc->m2m.vfd)) {
770*4882a593Smuzhiyun video_unregister_device(&fimc->m2m.vfd);
771*4882a593Smuzhiyun media_entity_cleanup(&fimc->m2m.vfd.entity);
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun }
774