12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25d718338SSungchun Kang /*
35d718338SSungchun Kang * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
45d718338SSungchun Kang * http://www.samsung.com
55d718338SSungchun Kang *
65d718338SSungchun Kang * Samsung EXYNOS5 SoC series G-Scaler driver
75d718338SSungchun Kang */
85d718338SSungchun Kang
95d718338SSungchun Kang #include <linux/module.h>
105d718338SSungchun Kang #include <linux/kernel.h>
115d718338SSungchun Kang #include <linux/types.h>
125d718338SSungchun Kang #include <linux/errno.h>
135d718338SSungchun Kang #include <linux/bug.h>
145d718338SSungchun Kang #include <linux/interrupt.h>
155d718338SSungchun Kang #include <linux/workqueue.h>
165d718338SSungchun Kang #include <linux/device.h>
175d718338SSungchun Kang #include <linux/platform_device.h>
185d718338SSungchun Kang #include <linux/list.h>
195d718338SSungchun Kang #include <linux/io.h>
205d718338SSungchun Kang #include <linux/slab.h>
215d718338SSungchun Kang #include <linux/clk.h>
225d718338SSungchun Kang
235d718338SSungchun Kang #include <media/v4l2-ioctl.h>
245d718338SSungchun Kang
255d718338SSungchun Kang #include "gsc-core.h"
265d718338SSungchun Kang
gsc_m2m_ctx_stop_req(struct gsc_ctx * ctx)275d718338SSungchun Kang static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
285d718338SSungchun Kang {
295d718338SSungchun Kang struct gsc_ctx *curr_ctx;
305d718338SSungchun Kang struct gsc_dev *gsc = ctx->gsc_dev;
315d718338SSungchun Kang int ret;
325d718338SSungchun Kang
335d718338SSungchun Kang curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
345d718338SSungchun Kang if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
355d718338SSungchun Kang return 0;
365d718338SSungchun Kang
375d718338SSungchun Kang gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
385d718338SSungchun Kang ret = wait_event_timeout(gsc->irq_queue,
395d718338SSungchun Kang !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
405d718338SSungchun Kang GSC_SHUTDOWN_TIMEOUT);
415d718338SSungchun Kang
425d718338SSungchun Kang return ret == 0 ? -ETIMEDOUT : ret;
435d718338SSungchun Kang }
445d718338SSungchun Kang
__gsc_m2m_job_abort(struct gsc_ctx * ctx)45d9315160SShaik Ameer Basha static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
46d9315160SShaik Ameer Basha {
47d9315160SShaik Ameer Basha int ret;
48d9315160SShaik Ameer Basha
49d9315160SShaik Ameer Basha ret = gsc_m2m_ctx_stop_req(ctx);
50d9315160SShaik Ameer Basha if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
51d9315160SShaik Ameer Basha gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
52d9315160SShaik Ameer Basha gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
53d9315160SShaik Ameer Basha }
54d9315160SShaik Ameer Basha }
55d9315160SShaik Ameer Basha
gsc_m2m_start_streaming(struct vb2_queue * q,unsigned int count)565d718338SSungchun Kang static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
575d718338SSungchun Kang {
585d718338SSungchun Kang struct gsc_ctx *ctx = q->drv_priv;
595d718338SSungchun Kang
60*59087b66SMauro Carvalho Chehab return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);
615d718338SSungchun Kang }
625d718338SSungchun Kang
__gsc_m2m_cleanup_queue(struct gsc_ctx * ctx)6376ca0824SJavier Martinez Canillas static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)
6476ca0824SJavier Martinez Canillas {
6576ca0824SJavier Martinez Canillas struct vb2_v4l2_buffer *src_vb, *dst_vb;
6676ca0824SJavier Martinez Canillas
6776ca0824SJavier Martinez Canillas while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
6876ca0824SJavier Martinez Canillas src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
6976ca0824SJavier Martinez Canillas v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
7076ca0824SJavier Martinez Canillas }
7176ca0824SJavier Martinez Canillas
7276ca0824SJavier Martinez Canillas while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {
7376ca0824SJavier Martinez Canillas dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
7476ca0824SJavier Martinez Canillas v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
7576ca0824SJavier Martinez Canillas }
7676ca0824SJavier Martinez Canillas }
7776ca0824SJavier Martinez Canillas
gsc_m2m_stop_streaming(struct vb2_queue * q)78e37559b2SHans Verkuil static void gsc_m2m_stop_streaming(struct vb2_queue *q)
795d718338SSungchun Kang {
805d718338SSungchun Kang struct gsc_ctx *ctx = q->drv_priv;
815d718338SSungchun Kang
82d9315160SShaik Ameer Basha __gsc_m2m_job_abort(ctx);
835d718338SSungchun Kang
8476ca0824SJavier Martinez Canillas __gsc_m2m_cleanup_queue(ctx);
8576ca0824SJavier Martinez Canillas
865d718338SSungchun Kang pm_runtime_put(&ctx->gsc_dev->pdev->dev);
875d718338SSungchun Kang }
885d718338SSungchun Kang
gsc_m2m_job_finish(struct gsc_ctx * ctx,int vb_state)895d718338SSungchun Kang void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
905d718338SSungchun Kang {
912d700715SJunghak Sung struct vb2_v4l2_buffer *src_vb, *dst_vb;
925d718338SSungchun Kang
935d718338SSungchun Kang if (!ctx || !ctx->m2m_ctx)
945d718338SSungchun Kang return;
955d718338SSungchun Kang
965d718338SSungchun Kang src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
975d718338SSungchun Kang dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
985d718338SSungchun Kang
995d718338SSungchun Kang if (src_vb && dst_vb) {
100d6dd645eSJunghak Sung dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
1012d700715SJunghak Sung dst_vb->timecode = src_vb->timecode;
1022d700715SJunghak Sung dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1032d700715SJunghak Sung dst_vb->flags |=
1042d700715SJunghak Sung src_vb->flags
105309f4d62SSakari Ailus & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
1069c303ec6SKamil Debski
1075d718338SSungchun Kang v4l2_m2m_buf_done(src_vb, vb_state);
1085d718338SSungchun Kang v4l2_m2m_buf_done(dst_vb, vb_state);
1095d718338SSungchun Kang
1105d718338SSungchun Kang v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
1115d718338SSungchun Kang ctx->m2m_ctx);
1125d718338SSungchun Kang }
1135d718338SSungchun Kang }
1145d718338SSungchun Kang
gsc_m2m_job_abort(void * priv)1155d718338SSungchun Kang static void gsc_m2m_job_abort(void *priv)
1165d718338SSungchun Kang {
117d9315160SShaik Ameer Basha __gsc_m2m_job_abort((struct gsc_ctx *)priv);
1185d718338SSungchun Kang }
1195d718338SSungchun Kang
gsc_get_bufs(struct gsc_ctx * ctx)120f60e160eSShaik Ameer Basha static int gsc_get_bufs(struct gsc_ctx *ctx)
1215d718338SSungchun Kang {
1225d718338SSungchun Kang struct gsc_frame *s_frame, *d_frame;
1232d700715SJunghak Sung struct vb2_v4l2_buffer *src_vb, *dst_vb;
1245d718338SSungchun Kang int ret;
1255d718338SSungchun Kang
1265d718338SSungchun Kang s_frame = &ctx->s_frame;
1275d718338SSungchun Kang d_frame = &ctx->d_frame;
1285d718338SSungchun Kang
129f60e160eSShaik Ameer Basha src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
1302d700715SJunghak Sung ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);
1315d718338SSungchun Kang if (ret)
1325d718338SSungchun Kang return ret;
1335d718338SSungchun Kang
134f60e160eSShaik Ameer Basha dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
1352d700715SJunghak Sung ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);
136f60e160eSShaik Ameer Basha if (ret)
137f60e160eSShaik Ameer Basha return ret;
138f60e160eSShaik Ameer Basha
139d6dd645eSJunghak Sung dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
140f60e160eSShaik Ameer Basha
141f60e160eSShaik Ameer Basha return 0;
1425d718338SSungchun Kang }
1435d718338SSungchun Kang
gsc_m2m_device_run(void * priv)1445d718338SSungchun Kang static void gsc_m2m_device_run(void *priv)
1455d718338SSungchun Kang {
1465d718338SSungchun Kang struct gsc_ctx *ctx = priv;
1475d718338SSungchun Kang struct gsc_dev *gsc;
1485d718338SSungchun Kang unsigned long flags;
1494bd0e030SShaik Ameer Basha int ret;
1505d718338SSungchun Kang bool is_set = false;
1515d718338SSungchun Kang
1525d718338SSungchun Kang if (WARN(!ctx, "null hardware context\n"))
1535d718338SSungchun Kang return;
1545d718338SSungchun Kang
1555d718338SSungchun Kang gsc = ctx->gsc_dev;
1565d718338SSungchun Kang spin_lock_irqsave(&gsc->slock, flags);
1575d718338SSungchun Kang
1585d718338SSungchun Kang set_bit(ST_M2M_PEND, &gsc->state);
1595d718338SSungchun Kang
1605d718338SSungchun Kang /* Reconfigure hardware if the context has changed. */
1615d718338SSungchun Kang if (gsc->m2m.ctx != ctx) {
1625d718338SSungchun Kang pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
1635d718338SSungchun Kang gsc->m2m.ctx, ctx);
1645d718338SSungchun Kang ctx->state |= GSC_PARAMS;
1655d718338SSungchun Kang gsc->m2m.ctx = ctx;
1665d718338SSungchun Kang }
1675d718338SSungchun Kang
168d9315160SShaik Ameer Basha is_set = ctx->state & GSC_CTX_STOP_REQ;
1695d718338SSungchun Kang if (is_set) {
170d9315160SShaik Ameer Basha ctx->state &= ~GSC_CTX_STOP_REQ;
171d9315160SShaik Ameer Basha ctx->state |= GSC_CTX_ABORT;
1725d718338SSungchun Kang wake_up(&gsc->irq_queue);
1735d718338SSungchun Kang goto put_device;
1745d718338SSungchun Kang }
1755d718338SSungchun Kang
176f60e160eSShaik Ameer Basha ret = gsc_get_bufs(ctx);
1775d718338SSungchun Kang if (ret) {
1785d718338SSungchun Kang pr_err("Wrong address");
1795d718338SSungchun Kang goto put_device;
1805d718338SSungchun Kang }
1815d718338SSungchun Kang
1825d718338SSungchun Kang gsc_set_prefbuf(gsc, &ctx->s_frame);
1835d718338SSungchun Kang gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
1845d718338SSungchun Kang gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
1855d718338SSungchun Kang
1865d718338SSungchun Kang if (ctx->state & GSC_PARAMS) {
1875d718338SSungchun Kang gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1885d718338SSungchun Kang gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
1895d718338SSungchun Kang gsc_hw_set_frm_done_irq_mask(gsc, false);
1905d718338SSungchun Kang gsc_hw_set_gsc_irq_enable(gsc, true);
1915d718338SSungchun Kang
1925d718338SSungchun Kang if (gsc_set_scaler_info(ctx)) {
1935d718338SSungchun Kang pr_err("Scaler setup error");
1945d718338SSungchun Kang goto put_device;
1955d718338SSungchun Kang }
1965d718338SSungchun Kang
1975d718338SSungchun Kang gsc_hw_set_input_path(ctx);
1985d718338SSungchun Kang gsc_hw_set_in_size(ctx);
1995d718338SSungchun Kang gsc_hw_set_in_image_format(ctx);
2005d718338SSungchun Kang
2015d718338SSungchun Kang gsc_hw_set_output_path(ctx);
2025d718338SSungchun Kang gsc_hw_set_out_size(ctx);
2035d718338SSungchun Kang gsc_hw_set_out_image_format(ctx);
2045d718338SSungchun Kang
2055d718338SSungchun Kang gsc_hw_set_prescaler(ctx);
2065d718338SSungchun Kang gsc_hw_set_mainscaler(ctx);
2075d718338SSungchun Kang gsc_hw_set_rotation(ctx);
2085d718338SSungchun Kang gsc_hw_set_global_alpha(ctx);
2095d718338SSungchun Kang }
2105d718338SSungchun Kang
2115d718338SSungchun Kang /* update shadow registers */
2125d718338SSungchun Kang gsc_hw_set_sfr_update(ctx);
2135d718338SSungchun Kang
2145d718338SSungchun Kang ctx->state &= ~GSC_PARAMS;
2155d718338SSungchun Kang gsc_hw_enable_control(gsc, true);
2165d718338SSungchun Kang
2175d718338SSungchun Kang spin_unlock_irqrestore(&gsc->slock, flags);
2185d718338SSungchun Kang return;
2195d718338SSungchun Kang
2205d718338SSungchun Kang put_device:
2215d718338SSungchun Kang ctx->state &= ~GSC_PARAMS;
2225d718338SSungchun Kang spin_unlock_irqrestore(&gsc->slock, flags);
2235d718338SSungchun Kang }
2245d718338SSungchun Kang
gsc_m2m_queue_setup(struct vb2_queue * vq,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])2255d718338SSungchun Kang static int gsc_m2m_queue_setup(struct vb2_queue *vq,
2265d718338SSungchun Kang unsigned int *num_buffers, unsigned int *num_planes,
22736c0f8b3SHans Verkuil unsigned int sizes[], struct device *alloc_devs[])
2285d718338SSungchun Kang {
2295d718338SSungchun Kang struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
2305d718338SSungchun Kang struct gsc_frame *frame;
2315d718338SSungchun Kang int i;
2325d718338SSungchun Kang
2335d718338SSungchun Kang frame = ctx_get_frame(ctx, vq->type);
2345d718338SSungchun Kang if (IS_ERR(frame))
2355d718338SSungchun Kang return PTR_ERR(frame);
2365d718338SSungchun Kang
2375d718338SSungchun Kang if (!frame->fmt)
2385d718338SSungchun Kang return -EINVAL;
2395d718338SSungchun Kang
2405d718338SSungchun Kang *num_planes = frame->fmt->num_planes;
241c781e4a5SHans Verkuil for (i = 0; i < frame->fmt->num_planes; i++)
2425d718338SSungchun Kang sizes[i] = frame->payload[i];
2435d718338SSungchun Kang return 0;
2445d718338SSungchun Kang }
2455d718338SSungchun Kang
gsc_m2m_buf_prepare(struct vb2_buffer * vb)2465d718338SSungchun Kang static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
2475d718338SSungchun Kang {
2485d718338SSungchun Kang struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2495d718338SSungchun Kang struct gsc_frame *frame;
2505d718338SSungchun Kang int i;
2515d718338SSungchun Kang
2525d718338SSungchun Kang frame = ctx_get_frame(ctx, vb->vb2_queue->type);
2535d718338SSungchun Kang if (IS_ERR(frame))
2545d718338SSungchun Kang return PTR_ERR(frame);
2555d718338SSungchun Kang
256b3ab1c60SEzequiel Garcia if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
2575d718338SSungchun Kang for (i = 0; i < frame->fmt->num_planes; i++)
2585d718338SSungchun Kang vb2_set_plane_payload(vb, i, frame->payload[i]);
2595d718338SSungchun Kang }
2605d718338SSungchun Kang
2615d718338SSungchun Kang return 0;
2625d718338SSungchun Kang }
2635d718338SSungchun Kang
gsc_m2m_buf_queue(struct vb2_buffer * vb)2645d718338SSungchun Kang static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
2655d718338SSungchun Kang {
2662d700715SJunghak Sung struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
2675d718338SSungchun Kang struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
2685d718338SSungchun Kang
2695d718338SSungchun Kang pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
2705d718338SSungchun Kang
2715d718338SSungchun Kang if (ctx->m2m_ctx)
2722d700715SJunghak Sung v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);
2735d718338SSungchun Kang }
2745d718338SSungchun Kang
275b7b361f0SJulia Lawall static const struct vb2_ops gsc_m2m_qops = {
2765d718338SSungchun Kang .queue_setup = gsc_m2m_queue_setup,
2775d718338SSungchun Kang .buf_prepare = gsc_m2m_buf_prepare,
2785d718338SSungchun Kang .buf_queue = gsc_m2m_buf_queue,
2795d718338SSungchun Kang .stop_streaming = gsc_m2m_stop_streaming,
2805d718338SSungchun Kang .start_streaming = gsc_m2m_start_streaming,
2815d718338SSungchun Kang };
2825d718338SSungchun Kang
gsc_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)2835d718338SSungchun Kang static int gsc_m2m_querycap(struct file *file, void *fh,
2845d718338SSungchun Kang struct v4l2_capability *cap)
2855d718338SSungchun Kang {
286c0decac1SMauro Carvalho Chehab strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));
287c0decac1SMauro Carvalho Chehab strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));
2885d718338SSungchun Kang return 0;
2895d718338SSungchun Kang }
2905d718338SSungchun Kang
gsc_m2m_enum_fmt(struct file * file,void * priv,struct v4l2_fmtdesc * f)2917e98b7b5SBoris Brezillon static int gsc_m2m_enum_fmt(struct file *file, void *priv,
2925d718338SSungchun Kang struct v4l2_fmtdesc *f)
2935d718338SSungchun Kang {
2947e98b7b5SBoris Brezillon return gsc_enum_fmt(f);
2955d718338SSungchun Kang }
2965d718338SSungchun Kang
gsc_m2m_g_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)2975d718338SSungchun Kang static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
2985d718338SSungchun Kang struct v4l2_format *f)
2995d718338SSungchun Kang {
3005d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3015d718338SSungchun Kang
3025d718338SSungchun Kang return gsc_g_fmt_mplane(ctx, f);
3035d718338SSungchun Kang }
3045d718338SSungchun Kang
gsc_m2m_try_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)3055d718338SSungchun Kang static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
3065d718338SSungchun Kang struct v4l2_format *f)
3075d718338SSungchun Kang {
3085d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3095d718338SSungchun Kang
3105d718338SSungchun Kang return gsc_try_fmt_mplane(ctx, f);
3115d718338SSungchun Kang }
3125d718338SSungchun Kang
gsc_m2m_s_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)3135d718338SSungchun Kang static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
3145d718338SSungchun Kang struct v4l2_format *f)
3155d718338SSungchun Kang {
3165d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3175d718338SSungchun Kang struct vb2_queue *vq;
3185d718338SSungchun Kang struct gsc_frame *frame;
3195d718338SSungchun Kang struct v4l2_pix_format_mplane *pix;
3205d718338SSungchun Kang int i, ret = 0;
3215d718338SSungchun Kang
3225d718338SSungchun Kang ret = gsc_m2m_try_fmt_mplane(file, fh, f);
3235d718338SSungchun Kang if (ret)
3245d718338SSungchun Kang return ret;
3255d718338SSungchun Kang
3265d718338SSungchun Kang vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
3275d718338SSungchun Kang
3285d718338SSungchun Kang if (vb2_is_streaming(vq)) {
3295d718338SSungchun Kang pr_err("queue (%d) busy", f->type);
3305d718338SSungchun Kang return -EBUSY;
3315d718338SSungchun Kang }
3325d718338SSungchun Kang
3335d718338SSungchun Kang if (V4L2_TYPE_IS_OUTPUT(f->type))
3345d718338SSungchun Kang frame = &ctx->s_frame;
3355d718338SSungchun Kang else
3365d718338SSungchun Kang frame = &ctx->d_frame;
3375d718338SSungchun Kang
3385d718338SSungchun Kang pix = &f->fmt.pix_mp;
3395d718338SSungchun Kang frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
3405d718338SSungchun Kang frame->colorspace = pix->colorspace;
3415d718338SSungchun Kang if (!frame->fmt)
3425d718338SSungchun Kang return -EINVAL;
3435d718338SSungchun Kang
3445d718338SSungchun Kang for (i = 0; i < frame->fmt->num_planes; i++)
3455d718338SSungchun Kang frame->payload[i] = pix->plane_fmt[i].sizeimage;
3465d718338SSungchun Kang
3475d718338SSungchun Kang gsc_set_frame_size(frame, pix->width, pix->height);
3485d718338SSungchun Kang
3495d718338SSungchun Kang if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
3505d718338SSungchun Kang gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
3515d718338SSungchun Kang else
3525d718338SSungchun Kang gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
3535d718338SSungchun Kang
3545d718338SSungchun Kang pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
3555d718338SSungchun Kang
3565d718338SSungchun Kang return 0;
3575d718338SSungchun Kang }
3585d718338SSungchun Kang
gsc_m2m_reqbufs(struct file * file,void * fh,struct v4l2_requestbuffers * reqbufs)3595d718338SSungchun Kang static int gsc_m2m_reqbufs(struct file *file, void *fh,
3605d718338SSungchun Kang struct v4l2_requestbuffers *reqbufs)
3615d718338SSungchun Kang {
3625d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3635d718338SSungchun Kang struct gsc_dev *gsc = ctx->gsc_dev;
3645d718338SSungchun Kang u32 max_cnt;
3655d718338SSungchun Kang
3665d718338SSungchun Kang max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
3675d718338SSungchun Kang gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
368daba4dfbSJavier Martinez Canillas if (reqbufs->count > max_cnt)
3695d718338SSungchun Kang return -EINVAL;
3705d718338SSungchun Kang
3715d718338SSungchun Kang return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
3725d718338SSungchun Kang }
3735d718338SSungchun Kang
gsc_m2m_expbuf(struct file * file,void * fh,struct v4l2_exportbuffer * eb)374371a664eSShaik Ameer Basha static int gsc_m2m_expbuf(struct file *file, void *fh,
375371a664eSShaik Ameer Basha struct v4l2_exportbuffer *eb)
376371a664eSShaik Ameer Basha {
377371a664eSShaik Ameer Basha struct gsc_ctx *ctx = fh_to_ctx(fh);
378371a664eSShaik Ameer Basha return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
379371a664eSShaik Ameer Basha }
380371a664eSShaik Ameer Basha
gsc_m2m_querybuf(struct file * file,void * fh,struct v4l2_buffer * buf)3815d718338SSungchun Kang static int gsc_m2m_querybuf(struct file *file, void *fh,
3825d718338SSungchun Kang struct v4l2_buffer *buf)
3835d718338SSungchun Kang {
3845d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3855d718338SSungchun Kang return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
3865d718338SSungchun Kang }
3875d718338SSungchun Kang
gsc_m2m_qbuf(struct file * file,void * fh,struct v4l2_buffer * buf)3885d718338SSungchun Kang static int gsc_m2m_qbuf(struct file *file, void *fh,
3895d718338SSungchun Kang struct v4l2_buffer *buf)
3905d718338SSungchun Kang {
3915d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3925d718338SSungchun Kang return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
3935d718338SSungchun Kang }
3945d718338SSungchun Kang
gsc_m2m_dqbuf(struct file * file,void * fh,struct v4l2_buffer * buf)3955d718338SSungchun Kang static int gsc_m2m_dqbuf(struct file *file, void *fh,
3965d718338SSungchun Kang struct v4l2_buffer *buf)
3975d718338SSungchun Kang {
3985d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
3995d718338SSungchun Kang return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
4005d718338SSungchun Kang }
4015d718338SSungchun Kang
gsc_m2m_streamon(struct file * file,void * fh,enum v4l2_buf_type type)4025d718338SSungchun Kang static int gsc_m2m_streamon(struct file *file, void *fh,
4035d718338SSungchun Kang enum v4l2_buf_type type)
4045d718338SSungchun Kang {
4055d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
4065d718338SSungchun Kang
4075d718338SSungchun Kang /* The source and target color format need to be set */
4085d718338SSungchun Kang if (V4L2_TYPE_IS_OUTPUT(type)) {
4095d718338SSungchun Kang if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
4105d718338SSungchun Kang return -EINVAL;
4115d718338SSungchun Kang } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
4125d718338SSungchun Kang return -EINVAL;
4135d718338SSungchun Kang }
4145d718338SSungchun Kang
4155d718338SSungchun Kang return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
4165d718338SSungchun Kang }
4175d718338SSungchun Kang
gsc_m2m_streamoff(struct file * file,void * fh,enum v4l2_buf_type type)4185d718338SSungchun Kang static int gsc_m2m_streamoff(struct file *file, void *fh,
4195d718338SSungchun Kang enum v4l2_buf_type type)
4205d718338SSungchun Kang {
4215d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
4225d718338SSungchun Kang return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
4235d718338SSungchun Kang }
4245d718338SSungchun Kang
4255d718338SSungchun Kang /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
is_rectangle_enclosed(struct v4l2_rect * a,struct v4l2_rect * b)4265d718338SSungchun Kang static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
4275d718338SSungchun Kang {
4285d718338SSungchun Kang if (a->left < b->left || a->top < b->top)
4295d718338SSungchun Kang return 0;
4305d718338SSungchun Kang
4315d718338SSungchun Kang if (a->left + a->width > b->left + b->width)
4325d718338SSungchun Kang return 0;
4335d718338SSungchun Kang
4345d718338SSungchun Kang if (a->top + a->height > b->top + b->height)
4355d718338SSungchun Kang return 0;
4365d718338SSungchun Kang
4375d718338SSungchun Kang return 1;
4385d718338SSungchun Kang }
4395d718338SSungchun Kang
gsc_m2m_g_selection(struct file * file,void * fh,struct v4l2_selection * s)4405d718338SSungchun Kang static int gsc_m2m_g_selection(struct file *file, void *fh,
4415d718338SSungchun Kang struct v4l2_selection *s)
4425d718338SSungchun Kang {
4435d718338SSungchun Kang struct gsc_frame *frame;
4445d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
4455d718338SSungchun Kang
446eaec420fSHans Verkuil if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
447eaec420fSHans Verkuil (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4485d718338SSungchun Kang return -EINVAL;
4495d718338SSungchun Kang
4505d718338SSungchun Kang frame = ctx_get_frame(ctx, s->type);
4515d718338SSungchun Kang if (IS_ERR(frame))
4525d718338SSungchun Kang return PTR_ERR(frame);
4535d718338SSungchun Kang
4545d718338SSungchun Kang switch (s->target) {
4555d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE_DEFAULT:
4565d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE_BOUNDS:
4575d718338SSungchun Kang case V4L2_SEL_TGT_CROP_BOUNDS:
4585d718338SSungchun Kang case V4L2_SEL_TGT_CROP_DEFAULT:
4595d718338SSungchun Kang s->r.left = 0;
4605d718338SSungchun Kang s->r.top = 0;
4615d718338SSungchun Kang s->r.width = frame->f_width;
4625d718338SSungchun Kang s->r.height = frame->f_height;
4635d718338SSungchun Kang return 0;
4645d718338SSungchun Kang
4655d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE:
4665d718338SSungchun Kang case V4L2_SEL_TGT_CROP:
4675d718338SSungchun Kang s->r.left = frame->crop.left;
4685d718338SSungchun Kang s->r.top = frame->crop.top;
4695d718338SSungchun Kang s->r.width = frame->crop.width;
4705d718338SSungchun Kang s->r.height = frame->crop.height;
4715d718338SSungchun Kang return 0;
4725d718338SSungchun Kang }
4735d718338SSungchun Kang
4745d718338SSungchun Kang return -EINVAL;
4755d718338SSungchun Kang }
4765d718338SSungchun Kang
gsc_m2m_s_selection(struct file * file,void * fh,struct v4l2_selection * s)4775d718338SSungchun Kang static int gsc_m2m_s_selection(struct file *file, void *fh,
4785d718338SSungchun Kang struct v4l2_selection *s)
4795d718338SSungchun Kang {
4805d718338SSungchun Kang struct gsc_frame *frame;
4815d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(fh);
4825d718338SSungchun Kang struct gsc_variant *variant = ctx->gsc_dev->variant;
4839ad763d0SHans Verkuil struct v4l2_selection sel = *s;
4845d718338SSungchun Kang int ret;
4855d718338SSungchun Kang
486eaec420fSHans Verkuil if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
487eaec420fSHans Verkuil (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
4885d718338SSungchun Kang return -EINVAL;
4895d718338SSungchun Kang
4909ad763d0SHans Verkuil ret = gsc_try_selection(ctx, &sel);
4915d718338SSungchun Kang if (ret)
4925d718338SSungchun Kang return ret;
4935d718338SSungchun Kang
4945d718338SSungchun Kang if (s->flags & V4L2_SEL_FLAG_LE &&
4959ad763d0SHans Verkuil !is_rectangle_enclosed(&sel.r, &s->r))
4965d718338SSungchun Kang return -ERANGE;
4975d718338SSungchun Kang
4985d718338SSungchun Kang if (s->flags & V4L2_SEL_FLAG_GE &&
4999ad763d0SHans Verkuil !is_rectangle_enclosed(&s->r, &sel.r))
5005d718338SSungchun Kang return -ERANGE;
5015d718338SSungchun Kang
5029ad763d0SHans Verkuil s->r = sel.r;
5035d718338SSungchun Kang
5045d718338SSungchun Kang switch (s->target) {
5055d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE_BOUNDS:
5065d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE_DEFAULT:
5075d718338SSungchun Kang case V4L2_SEL_TGT_COMPOSE:
5085d718338SSungchun Kang frame = &ctx->s_frame;
5095d718338SSungchun Kang break;
5105d718338SSungchun Kang
5115d718338SSungchun Kang case V4L2_SEL_TGT_CROP_BOUNDS:
5125d718338SSungchun Kang case V4L2_SEL_TGT_CROP:
5135d718338SSungchun Kang case V4L2_SEL_TGT_CROP_DEFAULT:
5145d718338SSungchun Kang frame = &ctx->d_frame;
5155d718338SSungchun Kang break;
5165d718338SSungchun Kang
5175d718338SSungchun Kang default:
5185d718338SSungchun Kang return -EINVAL;
5195d718338SSungchun Kang }
5205d718338SSungchun Kang
5215d718338SSungchun Kang /* Check to see if scaling ratio is within supported range */
5225d718338SSungchun Kang if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
5235d718338SSungchun Kang if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
5249ad763d0SHans Verkuil ret = gsc_check_scaler_ratio(variant, sel.r.width,
5259ad763d0SHans Verkuil sel.r.height, ctx->d_frame.crop.width,
5265d718338SSungchun Kang ctx->d_frame.crop.height,
5275d718338SSungchun Kang ctx->gsc_ctrls.rotate->val, ctx->out_path);
5285d718338SSungchun Kang } else {
5295d718338SSungchun Kang ret = gsc_check_scaler_ratio(variant,
5305d718338SSungchun Kang ctx->s_frame.crop.width,
5319ad763d0SHans Verkuil ctx->s_frame.crop.height, sel.r.width,
5329ad763d0SHans Verkuil sel.r.height, ctx->gsc_ctrls.rotate->val,
5335d718338SSungchun Kang ctx->out_path);
5345d718338SSungchun Kang }
5355d718338SSungchun Kang
5365d718338SSungchun Kang if (ret) {
5375d718338SSungchun Kang pr_err("Out of scaler range");
5385d718338SSungchun Kang return -EINVAL;
5395d718338SSungchun Kang }
5405d718338SSungchun Kang }
5415d718338SSungchun Kang
5429ad763d0SHans Verkuil frame->crop = sel.r;
5435d718338SSungchun Kang
5445d718338SSungchun Kang gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
5455d718338SSungchun Kang return 0;
5465d718338SSungchun Kang }
5475d718338SSungchun Kang
5485d718338SSungchun Kang static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
5495d718338SSungchun Kang .vidioc_querycap = gsc_m2m_querycap,
5507e98b7b5SBoris Brezillon .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt,
5517e98b7b5SBoris Brezillon .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt,
5525d718338SSungchun Kang .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane,
5535d718338SSungchun Kang .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane,
5545d718338SSungchun Kang .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane,
5555d718338SSungchun Kang .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane,
5565d718338SSungchun Kang .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,
5575d718338SSungchun Kang .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,
5585d718338SSungchun Kang .vidioc_reqbufs = gsc_m2m_reqbufs,
559371a664eSShaik Ameer Basha .vidioc_expbuf = gsc_m2m_expbuf,
5605d718338SSungchun Kang .vidioc_querybuf = gsc_m2m_querybuf,
5615d718338SSungchun Kang .vidioc_qbuf = gsc_m2m_qbuf,
5625d718338SSungchun Kang .vidioc_dqbuf = gsc_m2m_dqbuf,
5635d718338SSungchun Kang .vidioc_streamon = gsc_m2m_streamon,
5645d718338SSungchun Kang .vidioc_streamoff = gsc_m2m_streamoff,
5655d718338SSungchun Kang .vidioc_g_selection = gsc_m2m_g_selection,
5665d718338SSungchun Kang .vidioc_s_selection = gsc_m2m_s_selection
5675d718338SSungchun Kang };
5685d718338SSungchun Kang
queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)5695d718338SSungchun Kang static int queue_init(void *priv, struct vb2_queue *src_vq,
5705d718338SSungchun Kang struct vb2_queue *dst_vq)
5715d718338SSungchun Kang {
5725d718338SSungchun Kang struct gsc_ctx *ctx = priv;
5735d718338SSungchun Kang int ret;
5745d718338SSungchun Kang
5755d718338SSungchun Kang memset(src_vq, 0, sizeof(*src_vq));
5765d718338SSungchun Kang src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
577371a664eSShaik Ameer Basha src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
5785d718338SSungchun Kang src_vq->drv_priv = ctx;
5795d718338SSungchun Kang src_vq->ops = &gsc_m2m_qops;
5805d718338SSungchun Kang src_vq->mem_ops = &vb2_dma_contig_memops;
5815d718338SSungchun Kang src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
582ade48681SSakari Ailus src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
5830637f054SPrabhakar Lad src_vq->lock = &ctx->gsc_dev->lock;
584c781e4a5SHans Verkuil src_vq->dev = &ctx->gsc_dev->pdev->dev;
5855d718338SSungchun Kang
5865d718338SSungchun Kang ret = vb2_queue_init(src_vq);
5875d718338SSungchun Kang if (ret)
5885d718338SSungchun Kang return ret;
5895d718338SSungchun Kang
5905d718338SSungchun Kang memset(dst_vq, 0, sizeof(*dst_vq));
5915d718338SSungchun Kang dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
592371a664eSShaik Ameer Basha dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
5935d718338SSungchun Kang dst_vq->drv_priv = ctx;
5945d718338SSungchun Kang dst_vq->ops = &gsc_m2m_qops;
5955d718338SSungchun Kang dst_vq->mem_ops = &vb2_dma_contig_memops;
5965d718338SSungchun Kang dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
597ade48681SSakari Ailus dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
5980637f054SPrabhakar Lad dst_vq->lock = &ctx->gsc_dev->lock;
599c781e4a5SHans Verkuil dst_vq->dev = &ctx->gsc_dev->pdev->dev;
6005d718338SSungchun Kang
6015d718338SSungchun Kang return vb2_queue_init(dst_vq);
6025d718338SSungchun Kang }
6035d718338SSungchun Kang
gsc_m2m_open(struct file * file)6045d718338SSungchun Kang static int gsc_m2m_open(struct file *file)
6055d718338SSungchun Kang {
6065d718338SSungchun Kang struct gsc_dev *gsc = video_drvdata(file);
6075d718338SSungchun Kang struct gsc_ctx *ctx = NULL;
6085d718338SSungchun Kang int ret;
6095d718338SSungchun Kang
6105d718338SSungchun Kang pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
6115d718338SSungchun Kang
6125d718338SSungchun Kang if (mutex_lock_interruptible(&gsc->lock))
6135d718338SSungchun Kang return -ERESTARTSYS;
6145d718338SSungchun Kang
6155d718338SSungchun Kang ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
6165d718338SSungchun Kang if (!ctx) {
6175d718338SSungchun Kang ret = -ENOMEM;
6185d718338SSungchun Kang goto unlock;
6195d718338SSungchun Kang }
6205d718338SSungchun Kang
6215d718338SSungchun Kang v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
6225d718338SSungchun Kang ret = gsc_ctrls_create(ctx);
6235d718338SSungchun Kang if (ret)
6245d718338SSungchun Kang goto error_fh;
6255d718338SSungchun Kang
6265d718338SSungchun Kang /* Use separate control handler per file handle */
6275d718338SSungchun Kang ctx->fh.ctrl_handler = &ctx->ctrl_handler;
6285d718338SSungchun Kang file->private_data = &ctx->fh;
6295d718338SSungchun Kang v4l2_fh_add(&ctx->fh);
6305d718338SSungchun Kang
6315d718338SSungchun Kang ctx->gsc_dev = gsc;
6325d718338SSungchun Kang /* Default color format */
6335d718338SSungchun Kang ctx->s_frame.fmt = get_format(0);
6345d718338SSungchun Kang ctx->d_frame.fmt = get_format(0);
6355d718338SSungchun Kang /* Setup the device context for mem2mem mode. */
6365d718338SSungchun Kang ctx->state = GSC_CTX_M2M;
6375d718338SSungchun Kang ctx->flags = 0;
6385d718338SSungchun Kang ctx->in_path = GSC_DMA;
6395d718338SSungchun Kang ctx->out_path = GSC_DMA;
6405d718338SSungchun Kang
6415d718338SSungchun Kang ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
6425d718338SSungchun Kang if (IS_ERR(ctx->m2m_ctx)) {
6435d718338SSungchun Kang pr_err("Failed to initialize m2m context");
6445d718338SSungchun Kang ret = PTR_ERR(ctx->m2m_ctx);
6455d718338SSungchun Kang goto error_ctrls;
6465d718338SSungchun Kang }
6475d718338SSungchun Kang
6485d718338SSungchun Kang if (gsc->m2m.refcnt++ == 0)
6495d718338SSungchun Kang set_bit(ST_M2M_OPEN, &gsc->state);
6505d718338SSungchun Kang
6515d718338SSungchun Kang pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
6525d718338SSungchun Kang
6535d718338SSungchun Kang mutex_unlock(&gsc->lock);
6545d718338SSungchun Kang return 0;
6555d718338SSungchun Kang
6565d718338SSungchun Kang error_ctrls:
6575d718338SSungchun Kang gsc_ctrls_delete(ctx);
6585d718338SSungchun Kang v4l2_fh_del(&ctx->fh);
6593a07a827SShailendra Verma error_fh:
6605d718338SSungchun Kang v4l2_fh_exit(&ctx->fh);
6615d718338SSungchun Kang kfree(ctx);
6625d718338SSungchun Kang unlock:
6635d718338SSungchun Kang mutex_unlock(&gsc->lock);
6645d718338SSungchun Kang return ret;
6655d718338SSungchun Kang }
6665d718338SSungchun Kang
gsc_m2m_release(struct file * file)6675d718338SSungchun Kang static int gsc_m2m_release(struct file *file)
6685d718338SSungchun Kang {
6695d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
6705d718338SSungchun Kang struct gsc_dev *gsc = ctx->gsc_dev;
6715d718338SSungchun Kang
6725d718338SSungchun Kang pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
6735d718338SSungchun Kang task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
6745d718338SSungchun Kang
67598680180SSylwester Nawrocki mutex_lock(&gsc->lock);
6765d718338SSungchun Kang
6775d718338SSungchun Kang v4l2_m2m_ctx_release(ctx->m2m_ctx);
6785d718338SSungchun Kang gsc_ctrls_delete(ctx);
6795d718338SSungchun Kang v4l2_fh_del(&ctx->fh);
6805d718338SSungchun Kang v4l2_fh_exit(&ctx->fh);
6815d718338SSungchun Kang
6825d718338SSungchun Kang if (--gsc->m2m.refcnt <= 0)
6835d718338SSungchun Kang clear_bit(ST_M2M_OPEN, &gsc->state);
6845d718338SSungchun Kang kfree(ctx);
6855d718338SSungchun Kang
6865d718338SSungchun Kang mutex_unlock(&gsc->lock);
6875d718338SSungchun Kang return 0;
6885d718338SSungchun Kang }
6895d718338SSungchun Kang
gsc_m2m_poll(struct file * file,struct poll_table_struct * wait)690c23e0cb8SAl Viro static __poll_t gsc_m2m_poll(struct file *file,
6915d718338SSungchun Kang struct poll_table_struct *wait)
6925d718338SSungchun Kang {
6935d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
6945d718338SSungchun Kang struct gsc_dev *gsc = ctx->gsc_dev;
695c23e0cb8SAl Viro __poll_t ret;
6965d718338SSungchun Kang
6975d718338SSungchun Kang if (mutex_lock_interruptible(&gsc->lock))
698541b647aSMauro Carvalho Chehab return EPOLLERR;
6995d718338SSungchun Kang
7005d718338SSungchun Kang ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
7015d718338SSungchun Kang mutex_unlock(&gsc->lock);
7025d718338SSungchun Kang
7035d718338SSungchun Kang return ret;
7045d718338SSungchun Kang }
7055d718338SSungchun Kang
gsc_m2m_mmap(struct file * file,struct vm_area_struct * vma)7065d718338SSungchun Kang static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
7075d718338SSungchun Kang {
7085d718338SSungchun Kang struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
7095d718338SSungchun Kang struct gsc_dev *gsc = ctx->gsc_dev;
7105d718338SSungchun Kang int ret;
7115d718338SSungchun Kang
7125d718338SSungchun Kang if (mutex_lock_interruptible(&gsc->lock))
7135d718338SSungchun Kang return -ERESTARTSYS;
7145d718338SSungchun Kang
7155d718338SSungchun Kang ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
7165d718338SSungchun Kang mutex_unlock(&gsc->lock);
7175d718338SSungchun Kang
7185d718338SSungchun Kang return ret;
7195d718338SSungchun Kang }
7205d718338SSungchun Kang
7215d718338SSungchun Kang static const struct v4l2_file_operations gsc_m2m_fops = {
7225d718338SSungchun Kang .owner = THIS_MODULE,
7235d718338SSungchun Kang .open = gsc_m2m_open,
7245d718338SSungchun Kang .release = gsc_m2m_release,
7255d718338SSungchun Kang .poll = gsc_m2m_poll,
7265d718338SSungchun Kang .unlocked_ioctl = video_ioctl2,
7275d718338SSungchun Kang .mmap = gsc_m2m_mmap,
7285d718338SSungchun Kang };
7295d718338SSungchun Kang
730729ce68cSJulia Lawall static const struct v4l2_m2m_ops gsc_m2m_ops = {
7315d718338SSungchun Kang .device_run = gsc_m2m_device_run,
7325d718338SSungchun Kang .job_abort = gsc_m2m_job_abort,
7335d718338SSungchun Kang };
7345d718338SSungchun Kang
gsc_register_m2m_device(struct gsc_dev * gsc)7355d718338SSungchun Kang int gsc_register_m2m_device(struct gsc_dev *gsc)
7365d718338SSungchun Kang {
7375d718338SSungchun Kang struct platform_device *pdev;
7385d718338SSungchun Kang int ret;
7395d718338SSungchun Kang
7405d718338SSungchun Kang if (!gsc)
7415d718338SSungchun Kang return -ENODEV;
7425d718338SSungchun Kang
7435d718338SSungchun Kang pdev = gsc->pdev;
7445d718338SSungchun Kang
7455d718338SSungchun Kang gsc->vdev.fops = &gsc_m2m_fops;
7465d718338SSungchun Kang gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops;
7475d718338SSungchun Kang gsc->vdev.release = video_device_release_empty;
7485d718338SSungchun Kang gsc->vdev.lock = &gsc->lock;
74924fc681aSSylwester Nawrocki gsc->vdev.vfl_dir = VFL_DIR_M2M;
750d0b1c313SArun Kumar K gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
7511ddc8a97SBoris Brezillon gsc->vdev.device_caps = V4L2_CAP_STREAMING |
7521ddc8a97SBoris Brezillon V4L2_CAP_VIDEO_M2M_MPLANE;
7535d718338SSungchun Kang snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
7545d718338SSungchun Kang GSC_MODULE_NAME, gsc->id);
7555d718338SSungchun Kang
7565d718338SSungchun Kang video_set_drvdata(&gsc->vdev, gsc);
7575d718338SSungchun Kang
7585d718338SSungchun Kang gsc->m2m.vfd = &gsc->vdev;
7595d718338SSungchun Kang gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
7605d718338SSungchun Kang if (IS_ERR(gsc->m2m.m2m_dev)) {
7615d718338SSungchun Kang dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
762f4ca5030SJavier Martinez Canillas return PTR_ERR(gsc->m2m.m2m_dev);
7635d718338SSungchun Kang }
7645d718338SSungchun Kang
76570cad449SHans Verkuil ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);
7665d718338SSungchun Kang if (ret) {
7675d718338SSungchun Kang dev_err(&pdev->dev,
7685d718338SSungchun Kang "%s(): failed to register video device\n", __func__);
769f4ca5030SJavier Martinez Canillas goto err_m2m_release;
7705d718338SSungchun Kang }
7715d718338SSungchun Kang
7725d718338SSungchun Kang pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
7735d718338SSungchun Kang return 0;
7745d718338SSungchun Kang
775f4ca5030SJavier Martinez Canillas err_m2m_release:
7765d718338SSungchun Kang v4l2_m2m_release(gsc->m2m.m2m_dev);
7775d718338SSungchun Kang
7785d718338SSungchun Kang return ret;
7795d718338SSungchun Kang }
7805d718338SSungchun Kang
gsc_unregister_m2m_device(struct gsc_dev * gsc)7815d718338SSungchun Kang void gsc_unregister_m2m_device(struct gsc_dev *gsc)
7825d718338SSungchun Kang {
783115a16baSJavier Martinez Canillas if (gsc) {
7845d718338SSungchun Kang v4l2_m2m_release(gsc->m2m.m2m_dev);
785115a16baSJavier Martinez Canillas video_unregister_device(&gsc->vdev);
786115a16baSJavier Martinez Canillas }
7875d718338SSungchun Kang }
788