18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci * http://www.samsung.com 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Samsung EXYNOS5 SoC series G-Scaler driver 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/bug.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/list.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/clk.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "gsc-core.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct gsc_ctx *curr_ctx; 308c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 318c2ecf20Sopenharmony_ci int ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); 348c2ecf20Sopenharmony_ci if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx)) 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx); 388c2ecf20Sopenharmony_ci ret = wait_event_timeout(gsc->irq_queue, 398c2ecf20Sopenharmony_ci !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx), 408c2ecf20Sopenharmony_ci GSC_SHUTDOWN_TIMEOUT); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return ret == 0 ? -ETIMEDOUT : ret; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void __gsc_m2m_job_abort(struct gsc_ctx *ctx) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci ret = gsc_m2m_ctx_stop_req(ctx); 508c2ecf20Sopenharmony_ci if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) { 518c2ecf20Sopenharmony_ci gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx); 528c2ecf20Sopenharmony_ci gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = q->drv_priv; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) { 688c2ecf20Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); 698c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) { 738c2ecf20Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); 748c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void gsc_m2m_stop_streaming(struct vb2_queue *q) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = q->drv_priv; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci __gsc_m2m_job_abort(ctx); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci __gsc_m2m_cleanup_queue(ctx); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci pm_runtime_put(&ctx->gsc_dev->pdev->dev); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!ctx || !ctx->m2m_ctx) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); 978c2ecf20Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (src_vb && dst_vb) { 1008c2ecf20Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 1018c2ecf20Sopenharmony_ci dst_vb->timecode = src_vb->timecode; 1028c2ecf20Sopenharmony_ci dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 1038c2ecf20Sopenharmony_ci dst_vb->flags |= 1048c2ecf20Sopenharmony_ci src_vb->flags 1058c2ecf20Sopenharmony_ci & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(src_vb, vb_state); 1088c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, vb_state); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev, 1118c2ecf20Sopenharmony_ci ctx->m2m_ctx); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void gsc_m2m_job_abort(void *priv) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci __gsc_m2m_job_abort((struct gsc_ctx *)priv); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int gsc_get_bufs(struct gsc_ctx *ctx) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct gsc_frame *s_frame, *d_frame; 1238c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci s_frame = &ctx->s_frame; 1278c2ecf20Sopenharmony_ci d_frame = &ctx->d_frame; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); 1308c2ecf20Sopenharmony_ci ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr); 1318c2ecf20Sopenharmony_ci if (ret) 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); 1358c2ecf20Sopenharmony_ci ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void gsc_m2m_device_run(void *priv) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = priv; 1478c2ecf20Sopenharmony_ci struct gsc_dev *gsc; 1488c2ecf20Sopenharmony_ci unsigned long flags; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci bool is_set = false; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (WARN(!ctx, "null hardware context\n")) 1538c2ecf20Sopenharmony_ci return; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci gsc = ctx->gsc_dev; 1568c2ecf20Sopenharmony_ci spin_lock_irqsave(&gsc->slock, flags); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci set_bit(ST_M2M_PEND, &gsc->state); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Reconfigure hardware if the context has changed. */ 1618c2ecf20Sopenharmony_ci if (gsc->m2m.ctx != ctx) { 1628c2ecf20Sopenharmony_ci pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p", 1638c2ecf20Sopenharmony_ci gsc->m2m.ctx, ctx); 1648c2ecf20Sopenharmony_ci ctx->state |= GSC_PARAMS; 1658c2ecf20Sopenharmony_ci gsc->m2m.ctx = ctx; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci is_set = ctx->state & GSC_CTX_STOP_REQ; 1698c2ecf20Sopenharmony_ci if (is_set) { 1708c2ecf20Sopenharmony_ci ctx->state &= ~GSC_CTX_STOP_REQ; 1718c2ecf20Sopenharmony_ci ctx->state |= GSC_CTX_ABORT; 1728c2ecf20Sopenharmony_ci wake_up(&gsc->irq_queue); 1738c2ecf20Sopenharmony_ci goto put_device; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = gsc_get_bufs(ctx); 1778c2ecf20Sopenharmony_ci if (ret) { 1788c2ecf20Sopenharmony_ci pr_err("Wrong address"); 1798c2ecf20Sopenharmony_ci goto put_device; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci gsc_set_prefbuf(gsc, &ctx->s_frame); 1838c2ecf20Sopenharmony_ci gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM); 1848c2ecf20Sopenharmony_ci gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (ctx->state & GSC_PARAMS) { 1878c2ecf20Sopenharmony_ci gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false); 1888c2ecf20Sopenharmony_ci gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false); 1898c2ecf20Sopenharmony_ci gsc_hw_set_frm_done_irq_mask(gsc, false); 1908c2ecf20Sopenharmony_ci gsc_hw_set_gsc_irq_enable(gsc, true); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (gsc_set_scaler_info(ctx)) { 1938c2ecf20Sopenharmony_ci pr_err("Scaler setup error"); 1948c2ecf20Sopenharmony_ci goto put_device; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci gsc_hw_set_input_path(ctx); 1988c2ecf20Sopenharmony_ci gsc_hw_set_in_size(ctx); 1998c2ecf20Sopenharmony_ci gsc_hw_set_in_image_format(ctx); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci gsc_hw_set_output_path(ctx); 2028c2ecf20Sopenharmony_ci gsc_hw_set_out_size(ctx); 2038c2ecf20Sopenharmony_ci gsc_hw_set_out_image_format(ctx); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci gsc_hw_set_prescaler(ctx); 2068c2ecf20Sopenharmony_ci gsc_hw_set_mainscaler(ctx); 2078c2ecf20Sopenharmony_ci gsc_hw_set_rotation(ctx); 2088c2ecf20Sopenharmony_ci gsc_hw_set_global_alpha(ctx); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* update shadow registers */ 2128c2ecf20Sopenharmony_ci gsc_hw_set_sfr_update(ctx); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ctx->state &= ~GSC_PARAMS; 2158c2ecf20Sopenharmony_ci gsc_hw_enable_control(gsc, true); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gsc->slock, flags); 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciput_device: 2218c2ecf20Sopenharmony_ci ctx->state &= ~GSC_PARAMS; 2228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gsc->slock, flags); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int gsc_m2m_queue_setup(struct vb2_queue *vq, 2268c2ecf20Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 2278c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vq); 2308c2ecf20Sopenharmony_ci struct gsc_frame *frame; 2318c2ecf20Sopenharmony_ci int i; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci frame = ctx_get_frame(ctx, vq->type); 2348c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 2358c2ecf20Sopenharmony_ci return PTR_ERR(frame); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!frame->fmt) 2388c2ecf20Sopenharmony_ci return -EINVAL; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci *num_planes = frame->fmt->num_planes; 2418c2ecf20Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 2428c2ecf20Sopenharmony_ci sizes[i] = frame->payload[i]; 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int gsc_m2m_buf_prepare(struct vb2_buffer *vb) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 2498c2ecf20Sopenharmony_ci struct gsc_frame *frame; 2508c2ecf20Sopenharmony_ci int i; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci frame = ctx_get_frame(ctx, vb->vb2_queue->type); 2538c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 2548c2ecf20Sopenharmony_ci return PTR_ERR(frame); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { 2578c2ecf20Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 2588c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, i, frame->payload[i]); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void gsc_m2m_buf_queue(struct vb2_buffer *vb) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 2678c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (ctx->m2m_ctx) 2728c2ecf20Sopenharmony_ci v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct vb2_ops gsc_m2m_qops = { 2768c2ecf20Sopenharmony_ci .queue_setup = gsc_m2m_queue_setup, 2778c2ecf20Sopenharmony_ci .buf_prepare = gsc_m2m_buf_prepare, 2788c2ecf20Sopenharmony_ci .buf_queue = gsc_m2m_buf_queue, 2798c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 2808c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 2818c2ecf20Sopenharmony_ci .stop_streaming = gsc_m2m_stop_streaming, 2828c2ecf20Sopenharmony_ci .start_streaming = gsc_m2m_start_streaming, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int gsc_m2m_querycap(struct file *file, void *fh, 2868c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 2898c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); 2928c2ecf20Sopenharmony_ci strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); 2938c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 2948c2ecf20Sopenharmony_ci dev_name(&gsc->pdev->dev)); 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int gsc_m2m_enum_fmt(struct file *file, void *priv, 2998c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci return gsc_enum_fmt(f); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int gsc_m2m_g_fmt_mplane(struct file *file, void *fh, 3058c2ecf20Sopenharmony_ci struct v4l2_format *f) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return gsc_g_fmt_mplane(ctx, f); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int gsc_m2m_try_fmt_mplane(struct file *file, void *fh, 3138c2ecf20Sopenharmony_ci struct v4l2_format *f) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return gsc_try_fmt_mplane(ctx, f); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int gsc_m2m_s_fmt_mplane(struct file *file, void *fh, 3218c2ecf20Sopenharmony_ci struct v4l2_format *f) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3248c2ecf20Sopenharmony_ci struct vb2_queue *vq; 3258c2ecf20Sopenharmony_ci struct gsc_frame *frame; 3268c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 3278c2ecf20Sopenharmony_ci int i, ret = 0; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = gsc_m2m_try_fmt_mplane(file, fh, f); 3308c2ecf20Sopenharmony_ci if (ret) 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (vb2_is_streaming(vq)) { 3368c2ecf20Sopenharmony_ci pr_err("queue (%d) busy", f->type); 3378c2ecf20Sopenharmony_ci return -EBUSY; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(f->type)) 3418c2ecf20Sopenharmony_ci frame = &ctx->s_frame; 3428c2ecf20Sopenharmony_ci else 3438c2ecf20Sopenharmony_ci frame = &ctx->d_frame; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci pix = &f->fmt.pix_mp; 3468c2ecf20Sopenharmony_ci frame->fmt = find_fmt(&pix->pixelformat, NULL, 0); 3478c2ecf20Sopenharmony_ci frame->colorspace = pix->colorspace; 3488c2ecf20Sopenharmony_ci if (!frame->fmt) 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 3528c2ecf20Sopenharmony_ci frame->payload[i] = pix->plane_fmt[i].sizeimage; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci gsc_set_frame_size(frame, pix->width, pix->height); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 3578c2ecf20Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx); 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int gsc_m2m_reqbufs(struct file *file, void *fh, 3678c2ecf20Sopenharmony_ci struct v4l2_requestbuffers *reqbufs) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3708c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 3718c2ecf20Sopenharmony_ci u32 max_cnt; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? 3748c2ecf20Sopenharmony_ci gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt; 3758c2ecf20Sopenharmony_ci if (reqbufs->count > max_cnt) 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int gsc_m2m_expbuf(struct file *file, void *fh, 3828c2ecf20Sopenharmony_ci struct v4l2_exportbuffer *eb) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3858c2ecf20Sopenharmony_ci return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int gsc_m2m_querybuf(struct file *file, void *fh, 3898c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3928c2ecf20Sopenharmony_ci return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int gsc_m2m_qbuf(struct file *file, void *fh, 3968c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 3998c2ecf20Sopenharmony_ci return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int gsc_m2m_dqbuf(struct file *file, void *fh, 4038c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 4068c2ecf20Sopenharmony_ci return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int gsc_m2m_streamon(struct file *file, void *fh, 4108c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* The source and target color format need to be set */ 4158c2ecf20Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) { 4168c2ecf20Sopenharmony_ci if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx)) 4178c2ecf20Sopenharmony_ci return -EINVAL; 4188c2ecf20Sopenharmony_ci } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) { 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int gsc_m2m_streamoff(struct file *file, void *fh, 4268c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 4298c2ecf20Sopenharmony_ci return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ 4338c2ecf20Sopenharmony_cistatic int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci if (a->left < b->left || a->top < b->top) 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (a->left + a->width > b->left + b->width) 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (a->top + a->height > b->top + b->height) 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 1; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int gsc_m2m_g_selection(struct file *file, void *fh, 4488c2ecf20Sopenharmony_ci struct v4l2_selection *s) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct gsc_frame *frame; 4518c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 4548c2ecf20Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci frame = ctx_get_frame(ctx, s->type); 4588c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 4598c2ecf20Sopenharmony_ci return PTR_ERR(frame); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci switch (s->target) { 4628c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 4638c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 4648c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 4658c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 4668c2ecf20Sopenharmony_ci s->r.left = 0; 4678c2ecf20Sopenharmony_ci s->r.top = 0; 4688c2ecf20Sopenharmony_ci s->r.width = frame->f_width; 4698c2ecf20Sopenharmony_ci s->r.height = frame->f_height; 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 4738c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 4748c2ecf20Sopenharmony_ci s->r.left = frame->crop.left; 4758c2ecf20Sopenharmony_ci s->r.top = frame->crop.top; 4768c2ecf20Sopenharmony_ci s->r.width = frame->crop.width; 4778c2ecf20Sopenharmony_ci s->r.height = frame->crop.height; 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return -EINVAL; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int gsc_m2m_s_selection(struct file *file, void *fh, 4858c2ecf20Sopenharmony_ci struct v4l2_selection *s) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct gsc_frame *frame; 4888c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 4898c2ecf20Sopenharmony_ci struct gsc_variant *variant = ctx->gsc_dev->variant; 4908c2ecf20Sopenharmony_ci struct v4l2_selection sel = *s; 4918c2ecf20Sopenharmony_ci int ret; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 4948c2ecf20Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 4958c2ecf20Sopenharmony_ci return -EINVAL; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ret = gsc_try_selection(ctx, &sel); 4988c2ecf20Sopenharmony_ci if (ret) 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_LE && 5028c2ecf20Sopenharmony_ci !is_rectangle_enclosed(&sel.r, &s->r)) 5038c2ecf20Sopenharmony_ci return -ERANGE; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_GE && 5068c2ecf20Sopenharmony_ci !is_rectangle_enclosed(&s->r, &sel.r)) 5078c2ecf20Sopenharmony_ci return -ERANGE; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci s->r = sel.r; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci switch (s->target) { 5128c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 5138c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 5148c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 5158c2ecf20Sopenharmony_ci frame = &ctx->s_frame; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 5198c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 5208c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 5218c2ecf20Sopenharmony_ci frame = &ctx->d_frame; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci return -EINVAL; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Check to see if scaling ratio is within supported range */ 5298c2ecf20Sopenharmony_ci if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) { 5308c2ecf20Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 5318c2ecf20Sopenharmony_ci ret = gsc_check_scaler_ratio(variant, sel.r.width, 5328c2ecf20Sopenharmony_ci sel.r.height, ctx->d_frame.crop.width, 5338c2ecf20Sopenharmony_ci ctx->d_frame.crop.height, 5348c2ecf20Sopenharmony_ci ctx->gsc_ctrls.rotate->val, ctx->out_path); 5358c2ecf20Sopenharmony_ci } else { 5368c2ecf20Sopenharmony_ci ret = gsc_check_scaler_ratio(variant, 5378c2ecf20Sopenharmony_ci ctx->s_frame.crop.width, 5388c2ecf20Sopenharmony_ci ctx->s_frame.crop.height, sel.r.width, 5398c2ecf20Sopenharmony_ci sel.r.height, ctx->gsc_ctrls.rotate->val, 5408c2ecf20Sopenharmony_ci ctx->out_path); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (ret) { 5448c2ecf20Sopenharmony_ci pr_err("Out of scaler range"); 5458c2ecf20Sopenharmony_ci return -EINVAL; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci frame->crop = sel.r; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS, ctx); 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { 5568c2ecf20Sopenharmony_ci .vidioc_querycap = gsc_m2m_querycap, 5578c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt, 5588c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, 5598c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane, 5608c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane, 5618c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane, 5628c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane, 5638c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, 5648c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, 5658c2ecf20Sopenharmony_ci .vidioc_reqbufs = gsc_m2m_reqbufs, 5668c2ecf20Sopenharmony_ci .vidioc_expbuf = gsc_m2m_expbuf, 5678c2ecf20Sopenharmony_ci .vidioc_querybuf = gsc_m2m_querybuf, 5688c2ecf20Sopenharmony_ci .vidioc_qbuf = gsc_m2m_qbuf, 5698c2ecf20Sopenharmony_ci .vidioc_dqbuf = gsc_m2m_dqbuf, 5708c2ecf20Sopenharmony_ci .vidioc_streamon = gsc_m2m_streamon, 5718c2ecf20Sopenharmony_ci .vidioc_streamoff = gsc_m2m_streamoff, 5728c2ecf20Sopenharmony_ci .vidioc_g_selection = gsc_m2m_g_selection, 5738c2ecf20Sopenharmony_ci .vidioc_s_selection = gsc_m2m_s_selection 5748c2ecf20Sopenharmony_ci}; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 5778c2ecf20Sopenharmony_ci struct vb2_queue *dst_vq) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = priv; 5808c2ecf20Sopenharmony_ci int ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci memset(src_vq, 0, sizeof(*src_vq)); 5838c2ecf20Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 5848c2ecf20Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 5858c2ecf20Sopenharmony_ci src_vq->drv_priv = ctx; 5868c2ecf20Sopenharmony_ci src_vq->ops = &gsc_m2m_qops; 5878c2ecf20Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 5888c2ecf20Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 5898c2ecf20Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 5908c2ecf20Sopenharmony_ci src_vq->lock = &ctx->gsc_dev->lock; 5918c2ecf20Sopenharmony_ci src_vq->dev = &ctx->gsc_dev->pdev->dev; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = vb2_queue_init(src_vq); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci memset(dst_vq, 0, sizeof(*dst_vq)); 5988c2ecf20Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 5998c2ecf20Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 6008c2ecf20Sopenharmony_ci dst_vq->drv_priv = ctx; 6018c2ecf20Sopenharmony_ci dst_vq->ops = &gsc_m2m_qops; 6028c2ecf20Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 6038c2ecf20Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 6048c2ecf20Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 6058c2ecf20Sopenharmony_ci dst_vq->lock = &ctx->gsc_dev->lock; 6068c2ecf20Sopenharmony_ci dst_vq->dev = &ctx->gsc_dev->pdev->dev; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return vb2_queue_init(dst_vq); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int gsc_m2m_open(struct file *file) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct gsc_dev *gsc = video_drvdata(file); 6148c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = NULL; 6158c2ecf20Sopenharmony_ci int ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 6208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 6238c2ecf20Sopenharmony_ci if (!ctx) { 6248c2ecf20Sopenharmony_ci ret = -ENOMEM; 6258c2ecf20Sopenharmony_ci goto unlock; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci v4l2_fh_init(&ctx->fh, gsc->m2m.vfd); 6298c2ecf20Sopenharmony_ci ret = gsc_ctrls_create(ctx); 6308c2ecf20Sopenharmony_ci if (ret) 6318c2ecf20Sopenharmony_ci goto error_fh; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* Use separate control handler per file handle */ 6348c2ecf20Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 6358c2ecf20Sopenharmony_ci file->private_data = &ctx->fh; 6368c2ecf20Sopenharmony_ci v4l2_fh_add(&ctx->fh); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ctx->gsc_dev = gsc; 6398c2ecf20Sopenharmony_ci /* Default color format */ 6408c2ecf20Sopenharmony_ci ctx->s_frame.fmt = get_format(0); 6418c2ecf20Sopenharmony_ci ctx->d_frame.fmt = get_format(0); 6428c2ecf20Sopenharmony_ci /* Setup the device context for mem2mem mode. */ 6438c2ecf20Sopenharmony_ci ctx->state = GSC_CTX_M2M; 6448c2ecf20Sopenharmony_ci ctx->flags = 0; 6458c2ecf20Sopenharmony_ci ctx->in_path = GSC_DMA; 6468c2ecf20Sopenharmony_ci ctx->out_path = GSC_DMA; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init); 6498c2ecf20Sopenharmony_ci if (IS_ERR(ctx->m2m_ctx)) { 6508c2ecf20Sopenharmony_ci pr_err("Failed to initialize m2m context"); 6518c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->m2m_ctx); 6528c2ecf20Sopenharmony_ci goto error_ctrls; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (gsc->m2m.refcnt++ == 0) 6568c2ecf20Sopenharmony_ci set_bit(ST_M2M_OPEN, &gsc->state); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci mutex_unlock(&gsc->lock); 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cierror_ctrls: 6648c2ecf20Sopenharmony_ci gsc_ctrls_delete(ctx); 6658c2ecf20Sopenharmony_ci v4l2_fh_del(&ctx->fh); 6668c2ecf20Sopenharmony_cierror_fh: 6678c2ecf20Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 6688c2ecf20Sopenharmony_ci kfree(ctx); 6698c2ecf20Sopenharmony_ciunlock: 6708c2ecf20Sopenharmony_ci mutex_unlock(&gsc->lock); 6718c2ecf20Sopenharmony_ci return ret; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int gsc_m2m_release(struct file *file) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 6778c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci pr_debug("pid: %d, state: 0x%lx, refcnt= %d", 6808c2ecf20Sopenharmony_ci task_pid_nr(current), gsc->state, gsc->m2m.refcnt); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci mutex_lock(&gsc->lock); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci v4l2_m2m_ctx_release(ctx->m2m_ctx); 6858c2ecf20Sopenharmony_ci gsc_ctrls_delete(ctx); 6868c2ecf20Sopenharmony_ci v4l2_fh_del(&ctx->fh); 6878c2ecf20Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (--gsc->m2m.refcnt <= 0) 6908c2ecf20Sopenharmony_ci clear_bit(ST_M2M_OPEN, &gsc->state); 6918c2ecf20Sopenharmony_ci kfree(ctx); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci mutex_unlock(&gsc->lock); 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic __poll_t gsc_m2m_poll(struct file *file, 6988c2ecf20Sopenharmony_ci struct poll_table_struct *wait) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 7018c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 7028c2ecf20Sopenharmony_ci __poll_t ret; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 7058c2ecf20Sopenharmony_ci return EPOLLERR; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); 7088c2ecf20Sopenharmony_ci mutex_unlock(&gsc->lock); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return ret; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 7168c2ecf20Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 7178c2ecf20Sopenharmony_ci int ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 7208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); 7238c2ecf20Sopenharmony_ci mutex_unlock(&gsc->lock); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return ret; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations gsc_m2m_fops = { 7298c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7308c2ecf20Sopenharmony_ci .open = gsc_m2m_open, 7318c2ecf20Sopenharmony_ci .release = gsc_m2m_release, 7328c2ecf20Sopenharmony_ci .poll = gsc_m2m_poll, 7338c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 7348c2ecf20Sopenharmony_ci .mmap = gsc_m2m_mmap, 7358c2ecf20Sopenharmony_ci}; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops gsc_m2m_ops = { 7388c2ecf20Sopenharmony_ci .device_run = gsc_m2m_device_run, 7398c2ecf20Sopenharmony_ci .job_abort = gsc_m2m_job_abort, 7408c2ecf20Sopenharmony_ci}; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ciint gsc_register_m2m_device(struct gsc_dev *gsc) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct platform_device *pdev; 7458c2ecf20Sopenharmony_ci int ret; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!gsc) 7488c2ecf20Sopenharmony_ci return -ENODEV; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci pdev = gsc->pdev; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci gsc->vdev.fops = &gsc_m2m_fops; 7538c2ecf20Sopenharmony_ci gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops; 7548c2ecf20Sopenharmony_ci gsc->vdev.release = video_device_release_empty; 7558c2ecf20Sopenharmony_ci gsc->vdev.lock = &gsc->lock; 7568c2ecf20Sopenharmony_ci gsc->vdev.vfl_dir = VFL_DIR_M2M; 7578c2ecf20Sopenharmony_ci gsc->vdev.v4l2_dev = &gsc->v4l2_dev; 7588c2ecf20Sopenharmony_ci gsc->vdev.device_caps = V4L2_CAP_STREAMING | 7598c2ecf20Sopenharmony_ci V4L2_CAP_VIDEO_M2M_MPLANE; 7608c2ecf20Sopenharmony_ci snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", 7618c2ecf20Sopenharmony_ci GSC_MODULE_NAME, gsc->id); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci video_set_drvdata(&gsc->vdev, gsc); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci gsc->m2m.vfd = &gsc->vdev; 7668c2ecf20Sopenharmony_ci gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops); 7678c2ecf20Sopenharmony_ci if (IS_ERR(gsc->m2m.m2m_dev)) { 7688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n"); 7698c2ecf20Sopenharmony_ci return PTR_ERR(gsc->m2m.m2m_dev); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); 7738c2ecf20Sopenharmony_ci if (ret) { 7748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7758c2ecf20Sopenharmony_ci "%s(): failed to register video device\n", __func__); 7768c2ecf20Sopenharmony_ci goto err_m2m_release; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num); 7808c2ecf20Sopenharmony_ci return 0; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cierr_m2m_release: 7838c2ecf20Sopenharmony_ci v4l2_m2m_release(gsc->m2m.m2m_dev); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return ret; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_civoid gsc_unregister_m2m_device(struct gsc_dev *gsc) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci if (gsc) { 7918c2ecf20Sopenharmony_ci v4l2_m2m_release(gsc->m2m.m2m_dev); 7928c2ecf20Sopenharmony_ci video_unregister_device(&gsc->vdev); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci} 795