blob: b950c152fa28590cf2945a84043a014a9904f98a [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Sylwester Nawrocki97d97422012-05-08 15:51:24 -03002/*
3 * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
4 *
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -03005 * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
6 * Sylwester Nawrocki <s.nawrocki@samsung.com>
Sylwester Nawrocki97d97422012-05-08 15:51:24 -03007 */
8
9#include <linux/module.h>
10#include <linux/kernel.h>
11#include <linux/types.h>
12#include <linux/errno.h>
13#include <linux/bug.h>
14#include <linux/interrupt.h>
15#include <linux/device.h>
16#include <linux/platform_device.h>
17#include <linux/pm_runtime.h>
18#include <linux/list.h>
19#include <linux/io.h>
20#include <linux/slab.h>
21#include <linux/clk.h>
22#include <media/v4l2-ioctl.h>
Junghak Sungc1399902015-09-22 10:30:29 -030023#include <media/videobuf2-v4l2.h>
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030024#include <media/videobuf2-dma-contig.h>
25
Sylwester Nawrocki0f209562013-05-31 13:47:03 -030026#include "common.h"
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030027#include "fimc-core.h"
Sylwester Nawrockic83a1ff2012-05-02 06:14:49 -030028#include "fimc-reg.h"
Sylwester Nawrocki56fa1a62013-03-24 16:54:25 +010029#include "media-dev.h"
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030030
31static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
32{
33 if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
34 return FMT_FLAGS_M2M_IN;
35 else
36 return FMT_FLAGS_M2M_OUT;
37}
38
39void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
40{
Junghak Sung2d700712015-09-22 10:30:30 -030041 struct vb2_v4l2_buffer *src_vb, *dst_vb;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030042
Sylwester Nawrocki43894d82013-08-25 16:55:58 -030043 if (!ctx || !ctx->fh.m2m_ctx)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030044 return;
45
Sylwester Nawrocki43894d82013-08-25 16:55:58 -030046 src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
47 dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030048
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030049 if (src_vb)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030050 v4l2_m2m_buf_done(src_vb, vb_state);
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030051 if (dst_vb)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030052 v4l2_m2m_buf_done(dst_vb, vb_state);
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030053 if (src_vb && dst_vb)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030054 v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
Sylwester Nawrocki43894d82013-08-25 16:55:58 -030055 ctx->fh.m2m_ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030056}
57
58/* Complete the transaction which has been scheduled for execution. */
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030059static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030060{
61 struct fimc_dev *fimc = ctx->fimc_dev;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030062
63 if (!fimc_m2m_pending(fimc))
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030064 return;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030065
66 fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
67
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030068 wait_event_timeout(fimc->irq_queue,
69 !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
70 FIMC_SHUTDOWN_TIMEOUT);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030071}
72
73static int start_streaming(struct vb2_queue *q, unsigned int count)
74{
75 struct fimc_ctx *ctx = q->drv_priv;
76 int ret;
77
78 ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
79 return ret > 0 ? 0 : ret;
80}
81
Hans Verkuile37559b2014-04-17 02:47:21 -030082static void stop_streaming(struct vb2_queue *q)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030083{
84 struct fimc_ctx *ctx = q->drv_priv;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030085
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030086
Sylwester Nawrockif61d5002016-06-29 06:31:30 -030087 fimc_m2m_shutdown(ctx);
88 fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030089 pm_runtime_put(&ctx->fimc_dev->pdev->dev);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030090}
91
92static void fimc_device_run(void *priv)
93{
Junghak Sung2d700712015-09-22 10:30:30 -030094 struct vb2_v4l2_buffer *src_vb, *dst_vb;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -030095 struct fimc_ctx *ctx = priv;
96 struct fimc_frame *sf, *df;
97 struct fimc_dev *fimc;
98 unsigned long flags;
Shaik Ameer Bashaa63bafd2012-10-16 09:39:08 -030099 int ret;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300100
101 if (WARN(!ctx, "Null context\n"))
102 return;
103
104 fimc = ctx->fimc_dev;
105 spin_lock_irqsave(&fimc->slock, flags);
106
107 set_bit(ST_M2M_PEND, &fimc->state);
108 sf = &ctx->s_frame;
109 df = &ctx->d_frame;
110
111 if (ctx->state & FIMC_PARAMS) {
112 /* Prepare the DMA offsets for scaler */
113 fimc_prepare_dma_offset(ctx, sf);
114 fimc_prepare_dma_offset(ctx, df);
115 }
116
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300117 src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
Junghak Sung2d700712015-09-22 10:30:30 -0300118 ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->paddr);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300119 if (ret)
120 goto dma_unlock;
121
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300122 dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
Junghak Sung2d700712015-09-22 10:30:30 -0300123 ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->paddr);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300124 if (ret)
125 goto dma_unlock;
126
Junghak Sungd6dd6452015-11-03 08:16:37 -0200127 dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
Junghak Sung2d700712015-09-22 10:30:30 -0300128 dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
129 dst_vb->flags |=
130 src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
Sylwester Nawrockif0c24fd2013-04-24 13:07:49 -0300131
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300132 /* Reconfigure hardware if the context has changed. */
133 if (fimc->m2m.ctx != ctx) {
134 ctx->state |= FIMC_PARAMS;
135 fimc->m2m.ctx = ctx;
136 }
137
138 if (ctx->state & FIMC_PARAMS) {
139 fimc_set_yuv_order(ctx);
140 fimc_hw_set_input_path(ctx);
141 fimc_hw_set_in_dma(ctx);
142 ret = fimc_set_scaler_info(ctx);
143 if (ret)
144 goto dma_unlock;
145 fimc_hw_set_prescaler(ctx);
146 fimc_hw_set_mainscaler(ctx);
147 fimc_hw_set_target_format(ctx);
148 fimc_hw_set_rotation(ctx);
Sylwester Nawrocki9448ab72012-04-02 06:41:22 -0300149 fimc_hw_set_effect(ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300150 fimc_hw_set_out_dma(ctx);
Sylwester Nawrockie80cb1f2013-03-26 08:22:21 -0300151 if (fimc->drv_data->alpha_color)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300152 fimc_hw_set_rgb_alpha(ctx);
153 fimc_hw_set_output_path(ctx);
154 }
155 fimc_hw_set_input_addr(fimc, &sf->paddr);
156 fimc_hw_set_output_addr(fimc, &df->paddr, -1);
157
158 fimc_activate_capture(ctx);
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300159 ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300160 fimc_hw_activate_input_dma(fimc, true);
161
162dma_unlock:
163 spin_unlock_irqrestore(&fimc->slock, flags);
164}
165
166static void fimc_job_abort(void *priv)
167{
168 fimc_m2m_shutdown(priv);
169}
170
Hans Verkuildf9ecb02015-10-28 00:50:37 -0200171static int fimc_queue_setup(struct vb2_queue *vq,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300172 unsigned int *num_buffers, unsigned int *num_planes,
Hans Verkuil36c0f8b2016-04-15 09:15:05 -0300173 unsigned int sizes[], struct device *alloc_devs[])
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300174{
175 struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
176 struct fimc_frame *f;
177 int i;
178
179 f = ctx_get_frame(ctx, vq->type);
180 if (IS_ERR(f))
181 return PTR_ERR(f);
182 /*
Masahiro Yamadae1c05062015-07-07 10:14:59 +0900183 * Return number of non-contiguous planes (plane buffers)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300184 * depending on the configured color format.
185 */
186 if (!f->fmt)
187 return -EINVAL;
188
189 *num_planes = f->fmt->memplanes;
Hans Verkuil2548fee2016-02-16 07:30:19 -0200190 for (i = 0; i < f->fmt->memplanes; i++)
Nicolas Dufresneb9684a62014-03-25 17:53:32 -0300191 sizes[i] = f->payload[i];
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300192 return 0;
193}
194
195static int fimc_buf_prepare(struct vb2_buffer *vb)
196{
197 struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
198 struct fimc_frame *frame;
199 int i;
200
201 frame = ctx_get_frame(ctx, vb->vb2_queue->type);
202 if (IS_ERR(frame))
203 return PTR_ERR(frame);
204
205 for (i = 0; i < frame->fmt->memplanes; i++)
206 vb2_set_plane_payload(vb, i, frame->payload[i]);
207
208 return 0;
209}
210
211static void fimc_buf_queue(struct vb2_buffer *vb)
212{
Junghak Sung2d700712015-09-22 10:30:30 -0300213 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300214 struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
Junghak Sung2d700712015-09-22 10:30:30 -0300215 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300216}
217
Julia Lawallb7b361f2016-09-08 20:59:10 -0300218static const struct vb2_ops fimc_qops = {
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300219 .queue_setup = fimc_queue_setup,
220 .buf_prepare = fimc_buf_prepare,
221 .buf_queue = fimc_buf_queue,
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300222 .wait_prepare = vb2_ops_wait_prepare,
223 .wait_finish = vb2_ops_wait_finish,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300224 .stop_streaming = stop_streaming,
225 .start_streaming = start_streaming,
226};
227
228/*
229 * V4L2 ioctl handlers
230 */
231static int fimc_m2m_querycap(struct file *file, void *fh,
Sylwester Nawrockiaceb59e2013-04-23 11:36:04 -0300232 struct v4l2_capability *cap)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300233{
Sylwester Nawrockiaceb59e2013-04-23 11:36:04 -0300234 struct fimc_dev *fimc = video_drvdata(file);
Marek Szyprowskibc7eb5d2017-10-31 12:45:44 -0400235 unsigned int caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300236
Sylwester Nawrockiaceb59e2013-04-23 11:36:04 -0300237 __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300238 return 0;
239}
240
241static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
242 struct v4l2_fmtdesc *f)
243{
244 struct fimc_fmt *fmt;
245
246 fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
247 f->index);
248 if (!fmt)
249 return -EINVAL;
250
Mauro Carvalho Chehab85709cb2018-09-10 08:19:16 -0400251 strscpy(f->description, fmt->name, sizeof(f->description));
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300252 f->pixelformat = fmt->fourcc;
253 return 0;
254}
255
256static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
257 struct v4l2_format *f)
258{
259 struct fimc_ctx *ctx = fh_to_ctx(fh);
260 struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
261
262 if (IS_ERR(frame))
263 return PTR_ERR(frame);
264
Sylwester Nawrockifa8880b2013-01-11 06:36:19 -0300265 __fimc_get_format(frame, f);
266 return 0;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300267}
268
269static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
270{
271 struct fimc_dev *fimc = ctx->fimc_dev;
Sylwester Nawrocki405f2302012-08-02 10:27:46 -0300272 const struct fimc_variant *variant = fimc->variant;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300273 struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
274 struct fimc_fmt *fmt;
275 u32 max_w, mod_x, mod_y;
276
277 if (!IS_M2M(f->type))
278 return -EINVAL;
279
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300280 fmt = fimc_find_format(&pix->pixelformat, NULL,
281 get_m2m_fmt_flags(f->type), 0);
282 if (WARN(fmt == NULL, "Pixel format lookup failed"))
283 return -EINVAL;
284
285 if (pix->field == V4L2_FIELD_ANY)
286 pix->field = V4L2_FIELD_NONE;
287 else if (pix->field != V4L2_FIELD_NONE)
288 return -EINVAL;
289
290 if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
291 max_w = variant->pix_limit->scaler_dis_w;
292 mod_x = ffs(variant->min_inp_pixsize) - 1;
293 } else {
294 max_w = variant->pix_limit->out_rot_dis_w;
295 mod_x = ffs(variant->min_out_pixsize) - 1;
296 }
297
298 if (tiled_fmt(fmt)) {
299 mod_x = 6; /* 64 x 32 pixels tile */
300 mod_y = 5;
301 } else {
302 if (variant->min_vsize_align == 1)
303 mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
304 else
305 mod_y = ffs(variant->min_vsize_align) - 1;
306 }
307
308 v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
309 &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
310
311 fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
312 return 0;
313}
314
315static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
316 struct v4l2_format *f)
317{
318 struct fimc_ctx *ctx = fh_to_ctx(fh);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300319 return fimc_try_fmt_mplane(ctx, f);
320}
321
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300322static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt,
323 struct v4l2_pix_format_mplane *pixm)
324{
325 int i;
326
Nicolas Dufresne84113232014-03-25 17:52:31 -0300327 for (i = 0; i < fmt->memplanes; i++) {
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300328 frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;
329 frame->payload[i] = pixm->plane_fmt[i].sizeimage;
330 }
331
332 frame->f_width = pixm->width;
333 frame->f_height = pixm->height;
334 frame->o_width = pixm->width;
335 frame->o_height = pixm->height;
336 frame->width = pixm->width;
337 frame->height = pixm->height;
338 frame->offs_h = 0;
339 frame->offs_v = 0;
340 frame->fmt = fmt;
341}
342
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300343static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
344 struct v4l2_format *f)
345{
346 struct fimc_ctx *ctx = fh_to_ctx(fh);
347 struct fimc_dev *fimc = ctx->fimc_dev;
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300348 struct fimc_fmt *fmt;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300349 struct vb2_queue *vq;
350 struct fimc_frame *frame;
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300351 int ret;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300352
353 ret = fimc_try_fmt_mplane(ctx, f);
354 if (ret)
355 return ret;
356
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300357 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300358
359 if (vb2_is_busy(vq)) {
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300360 v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300361 return -EBUSY;
362 }
363
364 if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
365 frame = &ctx->s_frame;
366 else
367 frame = &ctx->d_frame;
368
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300369 fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL,
370 get_m2m_fmt_flags(f->type), 0);
371 if (!fmt)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300372 return -EINVAL;
373
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300374 __set_frame_format(frame, fmt, &f->fmt.pix_mp);
375
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300376 /* Update RGB Alpha control state and value range */
377 fimc_alpha_ctrl_update(ctx);
378
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300379 return 0;
380}
381
Hans Verkuil158efde2018-10-04 15:37:10 -0400382static int fimc_m2m_g_selection(struct file *file, void *fh,
383 struct v4l2_selection *s)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300384{
385 struct fimc_ctx *ctx = fh_to_ctx(fh);
386 struct fimc_frame *frame;
387
Hans Verkuil158efde2018-10-04 15:37:10 -0400388 frame = ctx_get_frame(ctx, s->type);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300389 if (IS_ERR(frame))
390 return PTR_ERR(frame);
391
Hans Verkuil158efde2018-10-04 15:37:10 -0400392 switch (s->target) {
393 case V4L2_SEL_TGT_CROP:
394 case V4L2_SEL_TGT_CROP_DEFAULT:
395 case V4L2_SEL_TGT_CROP_BOUNDS:
396 if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
397 return -EINVAL;
398 break;
399 case V4L2_SEL_TGT_COMPOSE:
400 case V4L2_SEL_TGT_COMPOSE_DEFAULT:
401 case V4L2_SEL_TGT_COMPOSE_BOUNDS:
402 if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
403 return -EINVAL;
404 break;
405 default:
406 return -EINVAL;
407 }
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300408
Hans Verkuil158efde2018-10-04 15:37:10 -0400409 switch (s->target) {
410 case V4L2_SEL_TGT_CROP:
411 case V4L2_SEL_TGT_COMPOSE:
412 s->r.left = frame->offs_h;
413 s->r.top = frame->offs_v;
414 s->r.width = frame->width;
415 s->r.height = frame->height;
416 break;
417 case V4L2_SEL_TGT_CROP_DEFAULT:
418 case V4L2_SEL_TGT_CROP_BOUNDS:
419 case V4L2_SEL_TGT_COMPOSE_DEFAULT:
420 case V4L2_SEL_TGT_COMPOSE_BOUNDS:
421 s->r.left = 0;
422 s->r.top = 0;
423 s->r.width = frame->o_width;
424 s->r.height = frame->o_height;
425 break;
426 default:
427 return -EINVAL;
428 }
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300429 return 0;
430}
431
Hans Verkuil158efde2018-10-04 15:37:10 -0400432static int fimc_m2m_try_selection(struct fimc_ctx *ctx,
433 struct v4l2_selection *s)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300434{
435 struct fimc_dev *fimc = ctx->fimc_dev;
436 struct fimc_frame *f;
437 u32 min_size, halign, depth = 0;
438 int i;
439
Hans Verkuil158efde2018-10-04 15:37:10 -0400440 if (s->r.top < 0 || s->r.left < 0) {
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300441 v4l2_err(&fimc->m2m.vfd,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300442 "doesn't support negative values for top & left\n");
443 return -EINVAL;
444 }
Hans Verkuil158efde2018-10-04 15:37:10 -0400445 if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300446 f = &ctx->d_frame;
Hans Verkuil158efde2018-10-04 15:37:10 -0400447 if (s->target != V4L2_SEL_TGT_COMPOSE)
448 return -EINVAL;
449 } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300450 f = &ctx->s_frame;
Hans Verkuil158efde2018-10-04 15:37:10 -0400451 if (s->target != V4L2_SEL_TGT_CROP)
452 return -EINVAL;
453 } else {
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300454 return -EINVAL;
Hans Verkuil158efde2018-10-04 15:37:10 -0400455 }
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300456
457 min_size = (f == &ctx->s_frame) ?
458 fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
459
460 /* Get pixel alignment constraints. */
461 if (fimc->variant->min_vsize_align == 1)
462 halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
463 else
464 halign = ffs(fimc->variant->min_vsize_align) - 1;
465
Nicolas Dufresne84113232014-03-25 17:52:31 -0300466 for (i = 0; i < f->fmt->memplanes; i++)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300467 depth += f->fmt->depth[i];
468
Hans Verkuil158efde2018-10-04 15:37:10 -0400469 v4l_bound_align_image(&s->r.width, min_size, f->o_width,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300470 ffs(min_size) - 1,
Hans Verkuil158efde2018-10-04 15:37:10 -0400471 &s->r.height, min_size, f->o_height,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300472 halign, 64/(ALIGN(depth, 8)));
473
474 /* adjust left/top if cropping rectangle is out of bounds */
Hans Verkuil158efde2018-10-04 15:37:10 -0400475 if (s->r.left + s->r.width > f->o_width)
476 s->r.left = f->o_width - s->r.width;
477 if (s->r.top + s->r.height > f->o_height)
478 s->r.top = f->o_height - s->r.height;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300479
Hans Verkuil158efde2018-10-04 15:37:10 -0400480 s->r.left = round_down(s->r.left, min_size);
481 s->r.top = round_down(s->r.top, fimc->variant->hor_offs_align);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300482
483 dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
Hans Verkuil158efde2018-10-04 15:37:10 -0400484 s->r.left, s->r.top, s->r.width, s->r.height,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300485 f->f_width, f->f_height);
486
487 return 0;
488}
489
Hans Verkuil158efde2018-10-04 15:37:10 -0400490static int fimc_m2m_s_selection(struct file *file, void *fh,
491 struct v4l2_selection *s)
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300492{
493 struct fimc_ctx *ctx = fh_to_ctx(fh);
494 struct fimc_dev *fimc = ctx->fimc_dev;
495 struct fimc_frame *f;
496 int ret;
497
Hans Verkuil158efde2018-10-04 15:37:10 -0400498 ret = fimc_m2m_try_selection(ctx, s);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300499 if (ret)
500 return ret;
501
Hans Verkuil158efde2018-10-04 15:37:10 -0400502 f = (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300503 &ctx->s_frame : &ctx->d_frame;
504
505 /* Check to see if scaling ratio is within supported range */
Hans Verkuil158efde2018-10-04 15:37:10 -0400506 if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
507 ret = fimc_check_scaler_ratio(ctx, s->r.width,
508 s->r.height, ctx->d_frame.width,
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300509 ctx->d_frame.height, ctx->rotation);
510 } else {
511 ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
Hans Verkuil158efde2018-10-04 15:37:10 -0400512 ctx->s_frame.height, s->r.width,
513 s->r.height, ctx->rotation);
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300514 }
515 if (ret) {
516 v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
517 return -EINVAL;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300518 }
519
Hans Verkuil158efde2018-10-04 15:37:10 -0400520 f->offs_h = s->r.left;
521 f->offs_v = s->r.top;
522 f->width = s->r.width;
523 f->height = s->r.height;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300524
525 fimc_ctx_state_set(FIMC_PARAMS, ctx);
526
527 return 0;
528}
529
530static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
531 .vidioc_querycap = fimc_m2m_querycap,
532 .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
533 .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
534 .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
535 .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
536 .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
537 .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
538 .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
539 .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300540 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
541 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
542 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
543 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
544 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
545 .vidioc_streamon = v4l2_m2m_ioctl_streamon,
546 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
Hans Verkuil158efde2018-10-04 15:37:10 -0400547 .vidioc_g_selection = fimc_m2m_g_selection,
548 .vidioc_s_selection = fimc_m2m_s_selection,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300549
550};
551
552static int queue_init(void *priv, struct vb2_queue *src_vq,
553 struct vb2_queue *dst_vq)
554{
555 struct fimc_ctx *ctx = priv;
556 int ret;
557
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300558 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
Tomasz Stanislawski9bd09fd2012-06-14 10:37:49 -0300559 src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300560 src_vq->drv_priv = ctx;
561 src_vq->ops = &fimc_qops;
562 src_vq->mem_ops = &vb2_dma_contig_memops;
563 src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
Sakari Ailusade48682014-02-25 19:12:19 -0300564 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300565 src_vq->lock = &ctx->fimc_dev->lock;
Hans Verkuil2548fee2016-02-16 07:30:19 -0200566 src_vq->dev = &ctx->fimc_dev->pdev->dev;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300567
568 ret = vb2_queue_init(src_vq);
569 if (ret)
570 return ret;
571
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300572 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
Tomasz Stanislawski9bd09fd2012-06-14 10:37:49 -0300573 dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300574 dst_vq->drv_priv = ctx;
575 dst_vq->ops = &fimc_qops;
576 dst_vq->mem_ops = &vb2_dma_contig_memops;
577 dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
Sakari Ailusade48682014-02-25 19:12:19 -0300578 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300579 dst_vq->lock = &ctx->fimc_dev->lock;
Hans Verkuil2548fee2016-02-16 07:30:19 -0200580 dst_vq->dev = &ctx->fimc_dev->pdev->dev;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300581
582 return vb2_queue_init(dst_vq);
583}
584
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300585static int fimc_m2m_set_default_format(struct fimc_ctx *ctx)
586{
587 struct v4l2_pix_format_mplane pixm = {
588 .pixelformat = V4L2_PIX_FMT_RGB32,
589 .width = 800,
590 .height = 600,
591 .plane_fmt[0] = {
592 .bytesperline = 800 * 4,
593 .sizeimage = 800 * 4 * 600,
594 },
595 };
596 struct fimc_fmt *fmt;
597
598 fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0);
599 if (!fmt)
600 return -EINVAL;
601
602 __set_frame_format(&ctx->s_frame, fmt, &pixm);
603 __set_frame_format(&ctx->d_frame, fmt, &pixm);
604
605 return 0;
606}
607
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300608static int fimc_m2m_open(struct file *file)
609{
610 struct fimc_dev *fimc = video_drvdata(file);
611 struct fimc_ctx *ctx;
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300612 int ret = -EBUSY;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300613
Sylwester Nawrockic4449142013-03-25 16:36:35 -0300614 pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300615
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300616 if (mutex_lock_interruptible(&fimc->lock))
617 return -ERESTARTSYS;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300618 /*
Sylwester Nawrockic4449142013-03-25 16:36:35 -0300619 * Don't allow simultaneous open() of the mem-to-mem and the
620 * capture video node that belong to same FIMC IP instance.
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300621 */
Sylwester Nawrockic4449142013-03-25 16:36:35 -0300622 if (test_bit(ST_CAPT_BUSY, &fimc->state))
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300623 goto unlock;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300624
Sachin Kamat26ee7f42012-08-17 03:28:27 -0300625 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300626 if (!ctx) {
627 ret = -ENOMEM;
628 goto unlock;
629 }
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300630 v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300631 ctx->fimc_dev = fimc;
632
633 /* Default color format */
634 ctx->s_frame.fmt = fimc_get_format(0);
635 ctx->d_frame.fmt = fimc_get_format(0);
636
637 ret = fimc_ctrls_create(ctx);
638 if (ret)
639 goto error_fh;
640
641 /* Use separate control handler per file handle */
Sylwester Nawrocki9448ab72012-04-02 06:41:22 -0300642 ctx->fh.ctrl_handler = &ctx->ctrls.handler;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300643 file->private_data = &ctx->fh;
644 v4l2_fh_add(&ctx->fh);
645
646 /* Setup the device context for memory-to-memory mode */
647 ctx->state = FIMC_CTX_M2M;
648 ctx->flags = 0;
Sylwester Nawrocki3d112d92012-04-26 06:26:29 -0300649 ctx->in_path = FIMC_IO_DMA;
650 ctx->out_path = FIMC_IO_DMA;
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300651 ctx->scaler.enabled = 1;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300652
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300653 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
654 if (IS_ERR(ctx->fh.m2m_ctx)) {
655 ret = PTR_ERR(ctx->fh.m2m_ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300656 goto error_c;
657 }
658
659 if (fimc->m2m.refcnt++ == 0)
660 set_bit(ST_M2M_RUN, &fimc->state);
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300661
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300662 ret = fimc_m2m_set_default_format(ctx);
663 if (ret < 0)
664 goto error_m2m_ctx;
665
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300666 mutex_unlock(&fimc->lock);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300667 return 0;
668
Sylwester Nawrocki81619ce2013-01-30 09:54:06 -0300669error_m2m_ctx:
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300670 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300671error_c:
672 fimc_ctrls_delete(ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300673 v4l2_fh_del(&ctx->fh);
Shailendra Verma5e6df4e2016-12-02 02:43:05 -0200674error_fh:
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300675 v4l2_fh_exit(&ctx->fh);
676 kfree(ctx);
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300677unlock:
678 mutex_unlock(&fimc->lock);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300679 return ret;
680}
681
682static int fimc_m2m_release(struct file *file)
683{
684 struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
685 struct fimc_dev *fimc = ctx->fimc_dev;
686
687 dbg("pid: %d, state: 0x%lx, refcnt= %d",
688 task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
689
Sylwester Nawrockiba6b3722012-11-22 11:12:16 -0300690 mutex_lock(&fimc->lock);
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300691
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300692 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300693 fimc_ctrls_delete(ctx);
694 v4l2_fh_del(&ctx->fh);
695 v4l2_fh_exit(&ctx->fh);
696
697 if (--fimc->m2m.refcnt <= 0)
698 clear_bit(ST_M2M_RUN, &fimc->state);
699 kfree(ctx);
Sylwester Nawrockic2d430a2012-06-27 09:56:17 -0300700
701 mutex_unlock(&fimc->lock);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300702 return 0;
703}
704
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300705static const struct v4l2_file_operations fimc_m2m_fops = {
706 .owner = THIS_MODULE,
707 .open = fimc_m2m_open,
708 .release = fimc_m2m_release,
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300709 .poll = v4l2_m2m_fop_poll,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300710 .unlocked_ioctl = video_ioctl2,
Sylwester Nawrocki43894d82013-08-25 16:55:58 -0300711 .mmap = v4l2_m2m_fop_mmap,
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300712};
713
Julia Lawall040ffe12017-08-06 04:25:20 -0400714static const struct v4l2_m2m_ops m2m_ops = {
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300715 .device_run = fimc_device_run,
716 .job_abort = fimc_job_abort,
717};
718
719int fimc_register_m2m_device(struct fimc_dev *fimc,
720 struct v4l2_device *v4l2_dev)
721{
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300722 struct video_device *vfd = &fimc->m2m.vfd;
723 int ret;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300724
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300725 fimc->v4l2_dev = v4l2_dev;
726
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300727 memset(vfd, 0, sizeof(*vfd));
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300728 vfd->fops = &fimc_m2m_fops;
729 vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
730 vfd->v4l2_dev = v4l2_dev;
731 vfd->minor = -1;
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300732 vfd->release = video_device_release_empty;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300733 vfd->lock = &fimc->lock;
Hans Verkuil954f3402012-09-05 06:05:50 -0300734 vfd->vfl_dir = VFL_DIR_M2M;
Hans Verkuil158efde2018-10-04 15:37:10 -0400735 set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300736
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300737 snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300738 video_set_drvdata(vfd, fimc);
739
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300740 fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
741 if (IS_ERR(fimc->m2m.m2m_dev)) {
742 v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300743 return PTR_ERR(fimc->m2m.m2m_dev);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300744 }
745
Mauro Carvalho Chehabab22e772015-12-11 07:44:40 -0200746 ret = media_entity_pads_init(&vfd->entity, 0, NULL);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300747 if (ret)
748 goto err_me;
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300749
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300750 ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
751 if (ret)
752 goto err_vd;
753
754 v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
755 vfd->name, video_device_node_name(vfd));
756 return 0;
757
758err_vd:
759 media_entity_cleanup(&vfd->entity);
760err_me:
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300761 v4l2_m2m_release(fimc->m2m.m2m_dev);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300762 return ret;
763}
764
765void fimc_unregister_m2m_device(struct fimc_dev *fimc)
766{
767 if (!fimc)
768 return;
769
770 if (fimc->m2m.m2m_dev)
771 v4l2_m2m_release(fimc->m2m.m2m_dev);
Sylwester Nawrockibbc52962012-07-26 07:59:11 -0300772
773 if (video_is_registered(&fimc->m2m.vfd)) {
774 video_unregister_device(&fimc->m2m.vfd);
775 media_entity_cleanup(&fimc->m2m.vfd.entity);
Sylwester Nawrocki97d97422012-05-08 15:51:24 -0300776 }
777}