162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci * http://www.samsung.com 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Samsung EXYNOS5 SoC series G-Scaler driver 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/bug.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/clk.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "gsc-core.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct gsc_ctx *curr_ctx; 3062306a36Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 3162306a36Sopenharmony_ci int ret; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); 3462306a36Sopenharmony_ci if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx)) 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx); 3862306a36Sopenharmony_ci ret = wait_event_timeout(gsc->irq_queue, 3962306a36Sopenharmony_ci !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx), 4062306a36Sopenharmony_ci GSC_SHUTDOWN_TIMEOUT); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return ret == 0 ? -ETIMEDOUT : ret; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void __gsc_m2m_job_abort(struct gsc_ctx *ctx) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int ret; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = gsc_m2m_ctx_stop_req(ctx); 5062306a36Sopenharmony_ci if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) { 5162306a36Sopenharmony_ci gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx); 5262306a36Sopenharmony_ci gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct gsc_ctx *ctx = q->drv_priv; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) { 6862306a36Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); 6962306a36Sopenharmony_ci v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) { 7362306a36Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); 7462306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void gsc_m2m_stop_streaming(struct vb2_queue *q) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct gsc_ctx *ctx = q->drv_priv; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci __gsc_m2m_job_abort(ctx); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci __gsc_m2m_cleanup_queue(ctx); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci pm_runtime_put(&ctx->gsc_dev->pdev->dev); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_civoid gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!ctx || !ctx->m2m_ctx) 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); 9762306a36Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (src_vb && dst_vb) { 10062306a36Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 10162306a36Sopenharmony_ci dst_vb->timecode = src_vb->timecode; 10262306a36Sopenharmony_ci dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 10362306a36Sopenharmony_ci dst_vb->flags |= 10462306a36Sopenharmony_ci src_vb->flags 10562306a36Sopenharmony_ci & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci v4l2_m2m_buf_done(src_vb, vb_state); 10862306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, vb_state); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev, 11162306a36Sopenharmony_ci ctx->m2m_ctx); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void gsc_m2m_job_abort(void *priv) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci __gsc_m2m_job_abort((struct gsc_ctx *)priv); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int gsc_get_bufs(struct gsc_ctx *ctx) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct gsc_frame *s_frame, *d_frame; 12362306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci s_frame = &ctx->s_frame; 12762306a36Sopenharmony_ci d_frame = &ctx->d_frame; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); 13062306a36Sopenharmony_ci ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr); 13162306a36Sopenharmony_ci if (ret) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); 13562306a36Sopenharmony_ci ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void gsc_m2m_device_run(void *priv) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct gsc_ctx *ctx = priv; 14762306a36Sopenharmony_ci struct gsc_dev *gsc; 14862306a36Sopenharmony_ci unsigned long flags; 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci bool is_set = false; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (WARN(!ctx, "null hardware context\n")) 15362306a36Sopenharmony_ci return; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci gsc = ctx->gsc_dev; 15662306a36Sopenharmony_ci spin_lock_irqsave(&gsc->slock, flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci set_bit(ST_M2M_PEND, &gsc->state); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Reconfigure hardware if the context has changed. */ 16162306a36Sopenharmony_ci if (gsc->m2m.ctx != ctx) { 16262306a36Sopenharmony_ci pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p", 16362306a36Sopenharmony_ci gsc->m2m.ctx, ctx); 16462306a36Sopenharmony_ci ctx->state |= GSC_PARAMS; 16562306a36Sopenharmony_ci gsc->m2m.ctx = ctx; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci is_set = ctx->state & GSC_CTX_STOP_REQ; 16962306a36Sopenharmony_ci if (is_set) { 17062306a36Sopenharmony_ci ctx->state &= ~GSC_CTX_STOP_REQ; 17162306a36Sopenharmony_ci ctx->state |= GSC_CTX_ABORT; 17262306a36Sopenharmony_ci wake_up(&gsc->irq_queue); 17362306a36Sopenharmony_ci goto put_device; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = gsc_get_bufs(ctx); 17762306a36Sopenharmony_ci if (ret) { 17862306a36Sopenharmony_ci pr_err("Wrong address"); 17962306a36Sopenharmony_ci goto put_device; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci gsc_set_prefbuf(gsc, &ctx->s_frame); 18362306a36Sopenharmony_ci gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM); 18462306a36Sopenharmony_ci gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (ctx->state & GSC_PARAMS) { 18762306a36Sopenharmony_ci gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false); 18862306a36Sopenharmony_ci gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false); 18962306a36Sopenharmony_ci gsc_hw_set_frm_done_irq_mask(gsc, false); 19062306a36Sopenharmony_ci gsc_hw_set_gsc_irq_enable(gsc, true); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (gsc_set_scaler_info(ctx)) { 19362306a36Sopenharmony_ci pr_err("Scaler setup error"); 19462306a36Sopenharmony_ci goto put_device; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci gsc_hw_set_input_path(ctx); 19862306a36Sopenharmony_ci gsc_hw_set_in_size(ctx); 19962306a36Sopenharmony_ci gsc_hw_set_in_image_format(ctx); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci gsc_hw_set_output_path(ctx); 20262306a36Sopenharmony_ci gsc_hw_set_out_size(ctx); 20362306a36Sopenharmony_ci gsc_hw_set_out_image_format(ctx); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci gsc_hw_set_prescaler(ctx); 20662306a36Sopenharmony_ci gsc_hw_set_mainscaler(ctx); 20762306a36Sopenharmony_ci gsc_hw_set_rotation(ctx); 20862306a36Sopenharmony_ci gsc_hw_set_global_alpha(ctx); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* update shadow registers */ 21262306a36Sopenharmony_ci gsc_hw_set_sfr_update(ctx); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ctx->state &= ~GSC_PARAMS; 21562306a36Sopenharmony_ci gsc_hw_enable_control(gsc, true); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_unlock_irqrestore(&gsc->slock, flags); 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciput_device: 22162306a36Sopenharmony_ci ctx->state &= ~GSC_PARAMS; 22262306a36Sopenharmony_ci spin_unlock_irqrestore(&gsc->slock, flags); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int gsc_m2m_queue_setup(struct vb2_queue *vq, 22662306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 22762306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vq); 23062306a36Sopenharmony_ci struct gsc_frame *frame; 23162306a36Sopenharmony_ci int i; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci frame = ctx_get_frame(ctx, vq->type); 23462306a36Sopenharmony_ci if (IS_ERR(frame)) 23562306a36Sopenharmony_ci return PTR_ERR(frame); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!frame->fmt) 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci *num_planes = frame->fmt->num_planes; 24162306a36Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 24262306a36Sopenharmony_ci sizes[i] = frame->payload[i]; 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int gsc_m2m_buf_prepare(struct vb2_buffer *vb) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 24962306a36Sopenharmony_ci struct gsc_frame *frame; 25062306a36Sopenharmony_ci int i; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci frame = ctx_get_frame(ctx, vb->vb2_queue->type); 25362306a36Sopenharmony_ci if (IS_ERR(frame)) 25462306a36Sopenharmony_ci return PTR_ERR(frame); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { 25762306a36Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 25862306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, frame->payload[i]); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void gsc_m2m_buf_queue(struct vb2_buffer *vb) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 26762306a36Sopenharmony_ci struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ctx->m2m_ctx) 27262306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const struct vb2_ops gsc_m2m_qops = { 27662306a36Sopenharmony_ci .queue_setup = gsc_m2m_queue_setup, 27762306a36Sopenharmony_ci .buf_prepare = gsc_m2m_buf_prepare, 27862306a36Sopenharmony_ci .buf_queue = gsc_m2m_buf_queue, 27962306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 28062306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 28162306a36Sopenharmony_ci .stop_streaming = gsc_m2m_stop_streaming, 28262306a36Sopenharmony_ci .start_streaming = gsc_m2m_start_streaming, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int gsc_m2m_querycap(struct file *file, void *fh, 28662306a36Sopenharmony_ci struct v4l2_capability *cap) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); 28962306a36Sopenharmony_ci strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int gsc_m2m_enum_fmt(struct file *file, void *priv, 29462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return gsc_enum_fmt(f); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int gsc_m2m_g_fmt_mplane(struct file *file, void *fh, 30062306a36Sopenharmony_ci struct v4l2_format *f) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return gsc_g_fmt_mplane(ctx, f); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int gsc_m2m_try_fmt_mplane(struct file *file, void *fh, 30862306a36Sopenharmony_ci struct v4l2_format *f) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return gsc_try_fmt_mplane(ctx, f); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int gsc_m2m_s_fmt_mplane(struct file *file, void *fh, 31662306a36Sopenharmony_ci struct v4l2_format *f) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 31962306a36Sopenharmony_ci struct vb2_queue *vq; 32062306a36Sopenharmony_ci struct gsc_frame *frame; 32162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 32262306a36Sopenharmony_ci int i, ret = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = gsc_m2m_try_fmt_mplane(file, fh, f); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (vb2_is_streaming(vq)) { 33162306a36Sopenharmony_ci pr_err("queue (%d) busy", f->type); 33262306a36Sopenharmony_ci return -EBUSY; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(f->type)) 33662306a36Sopenharmony_ci frame = &ctx->s_frame; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci frame = &ctx->d_frame; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci pix = &f->fmt.pix_mp; 34162306a36Sopenharmony_ci frame->fmt = find_fmt(&pix->pixelformat, NULL, 0); 34262306a36Sopenharmony_ci frame->colorspace = pix->colorspace; 34362306a36Sopenharmony_ci if (!frame->fmt) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < frame->fmt->num_planes; i++) 34762306a36Sopenharmony_ci frame->payload[i] = pix->plane_fmt[i].sizeimage; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci gsc_set_frame_size(frame, pix->width, pix->height); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 35262306a36Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx); 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int gsc_m2m_reqbufs(struct file *file, void *fh, 36262306a36Sopenharmony_ci struct v4l2_requestbuffers *reqbufs) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 36562306a36Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 36662306a36Sopenharmony_ci u32 max_cnt; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? 36962306a36Sopenharmony_ci gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt; 37062306a36Sopenharmony_ci if (reqbufs->count > max_cnt) 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int gsc_m2m_expbuf(struct file *file, void *fh, 37762306a36Sopenharmony_ci struct v4l2_exportbuffer *eb) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 38062306a36Sopenharmony_ci return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int gsc_m2m_querybuf(struct file *file, void *fh, 38462306a36Sopenharmony_ci struct v4l2_buffer *buf) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 38762306a36Sopenharmony_ci return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int gsc_m2m_qbuf(struct file *file, void *fh, 39162306a36Sopenharmony_ci struct v4l2_buffer *buf) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 39462306a36Sopenharmony_ci return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int gsc_m2m_dqbuf(struct file *file, void *fh, 39862306a36Sopenharmony_ci struct v4l2_buffer *buf) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 40162306a36Sopenharmony_ci return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int gsc_m2m_streamon(struct file *file, void *fh, 40562306a36Sopenharmony_ci enum v4l2_buf_type type) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* The source and target color format need to be set */ 41062306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) { 41162306a36Sopenharmony_ci if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx)) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) { 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int gsc_m2m_streamoff(struct file *file, void *fh, 42162306a36Sopenharmony_ci enum v4l2_buf_type type) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 42462306a36Sopenharmony_ci return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ 42862306a36Sopenharmony_cistatic int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci if (a->left < b->left || a->top < b->top) 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (a->left + a->width > b->left + b->width) 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (a->top + a->height > b->top + b->height) 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 1; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int gsc_m2m_g_selection(struct file *file, void *fh, 44362306a36Sopenharmony_ci struct v4l2_selection *s) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct gsc_frame *frame; 44662306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 44962306a36Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 45062306a36Sopenharmony_ci return -EINVAL; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci frame = ctx_get_frame(ctx, s->type); 45362306a36Sopenharmony_ci if (IS_ERR(frame)) 45462306a36Sopenharmony_ci return PTR_ERR(frame); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci switch (s->target) { 45762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 45862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 45962306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 46062306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 46162306a36Sopenharmony_ci s->r.left = 0; 46262306a36Sopenharmony_ci s->r.top = 0; 46362306a36Sopenharmony_ci s->r.width = frame->f_width; 46462306a36Sopenharmony_ci s->r.height = frame->f_height; 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 46862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 46962306a36Sopenharmony_ci s->r.left = frame->crop.left; 47062306a36Sopenharmony_ci s->r.top = frame->crop.top; 47162306a36Sopenharmony_ci s->r.width = frame->crop.width; 47262306a36Sopenharmony_ci s->r.height = frame->crop.height; 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return -EINVAL; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int gsc_m2m_s_selection(struct file *file, void *fh, 48062306a36Sopenharmony_ci struct v4l2_selection *s) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct gsc_frame *frame; 48362306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(fh); 48462306a36Sopenharmony_ci struct gsc_variant *variant = ctx->gsc_dev->variant; 48562306a36Sopenharmony_ci struct v4l2_selection sel = *s; 48662306a36Sopenharmony_ci int ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 48962306a36Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 49062306a36Sopenharmony_ci return -EINVAL; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = gsc_try_selection(ctx, &sel); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_LE && 49762306a36Sopenharmony_ci !is_rectangle_enclosed(&sel.r, &s->r)) 49862306a36Sopenharmony_ci return -ERANGE; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_GE && 50162306a36Sopenharmony_ci !is_rectangle_enclosed(&s->r, &sel.r)) 50262306a36Sopenharmony_ci return -ERANGE; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci s->r = sel.r; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci switch (s->target) { 50762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 50862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 50962306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 51062306a36Sopenharmony_ci frame = &ctx->s_frame; 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 51462306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 51562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 51662306a36Sopenharmony_ci frame = &ctx->d_frame; 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci default: 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Check to see if scaling ratio is within supported range */ 52462306a36Sopenharmony_ci if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) { 52562306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 52662306a36Sopenharmony_ci ret = gsc_check_scaler_ratio(variant, sel.r.width, 52762306a36Sopenharmony_ci sel.r.height, ctx->d_frame.crop.width, 52862306a36Sopenharmony_ci ctx->d_frame.crop.height, 52962306a36Sopenharmony_ci ctx->gsc_ctrls.rotate->val, ctx->out_path); 53062306a36Sopenharmony_ci } else { 53162306a36Sopenharmony_ci ret = gsc_check_scaler_ratio(variant, 53262306a36Sopenharmony_ci ctx->s_frame.crop.width, 53362306a36Sopenharmony_ci ctx->s_frame.crop.height, sel.r.width, 53462306a36Sopenharmony_ci sel.r.height, ctx->gsc_ctrls.rotate->val, 53562306a36Sopenharmony_ci ctx->out_path); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (ret) { 53962306a36Sopenharmony_ci pr_err("Out of scaler range"); 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci frame->crop = sel.r; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci gsc_ctx_state_lock_set(GSC_PARAMS, ctx); 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { 55162306a36Sopenharmony_ci .vidioc_querycap = gsc_m2m_querycap, 55262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt, 55362306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, 55462306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane, 55562306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane, 55662306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane, 55762306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane, 55862306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, 55962306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, 56062306a36Sopenharmony_ci .vidioc_reqbufs = gsc_m2m_reqbufs, 56162306a36Sopenharmony_ci .vidioc_expbuf = gsc_m2m_expbuf, 56262306a36Sopenharmony_ci .vidioc_querybuf = gsc_m2m_querybuf, 56362306a36Sopenharmony_ci .vidioc_qbuf = gsc_m2m_qbuf, 56462306a36Sopenharmony_ci .vidioc_dqbuf = gsc_m2m_dqbuf, 56562306a36Sopenharmony_ci .vidioc_streamon = gsc_m2m_streamon, 56662306a36Sopenharmony_ci .vidioc_streamoff = gsc_m2m_streamoff, 56762306a36Sopenharmony_ci .vidioc_g_selection = gsc_m2m_g_selection, 56862306a36Sopenharmony_ci .vidioc_s_selection = gsc_m2m_s_selection 56962306a36Sopenharmony_ci}; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 57262306a36Sopenharmony_ci struct vb2_queue *dst_vq) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct gsc_ctx *ctx = priv; 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci memset(src_vq, 0, sizeof(*src_vq)); 57862306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 57962306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 58062306a36Sopenharmony_ci src_vq->drv_priv = ctx; 58162306a36Sopenharmony_ci src_vq->ops = &gsc_m2m_qops; 58262306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 58362306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 58462306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 58562306a36Sopenharmony_ci src_vq->lock = &ctx->gsc_dev->lock; 58662306a36Sopenharmony_ci src_vq->dev = &ctx->gsc_dev->pdev->dev; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 58962306a36Sopenharmony_ci if (ret) 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci memset(dst_vq, 0, sizeof(*dst_vq)); 59362306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 59462306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 59562306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 59662306a36Sopenharmony_ci dst_vq->ops = &gsc_m2m_qops; 59762306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 59862306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 59962306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 60062306a36Sopenharmony_ci dst_vq->lock = &ctx->gsc_dev->lock; 60162306a36Sopenharmony_ci dst_vq->dev = &ctx->gsc_dev->pdev->dev; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int gsc_m2m_open(struct file *file) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct gsc_dev *gsc = video_drvdata(file); 60962306a36Sopenharmony_ci struct gsc_ctx *ctx = NULL; 61062306a36Sopenharmony_ci int ret; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 61562306a36Sopenharmony_ci return -ERESTARTSYS; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 61862306a36Sopenharmony_ci if (!ctx) { 61962306a36Sopenharmony_ci ret = -ENOMEM; 62062306a36Sopenharmony_ci goto unlock; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, gsc->m2m.vfd); 62462306a36Sopenharmony_ci ret = gsc_ctrls_create(ctx); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci goto error_fh; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Use separate control handler per file handle */ 62962306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 63062306a36Sopenharmony_ci file->private_data = &ctx->fh; 63162306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ctx->gsc_dev = gsc; 63462306a36Sopenharmony_ci /* Default color format */ 63562306a36Sopenharmony_ci ctx->s_frame.fmt = get_format(0); 63662306a36Sopenharmony_ci ctx->d_frame.fmt = get_format(0); 63762306a36Sopenharmony_ci /* Setup the device context for mem2mem mode. */ 63862306a36Sopenharmony_ci ctx->state = GSC_CTX_M2M; 63962306a36Sopenharmony_ci ctx->flags = 0; 64062306a36Sopenharmony_ci ctx->in_path = GSC_DMA; 64162306a36Sopenharmony_ci ctx->out_path = GSC_DMA; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init); 64462306a36Sopenharmony_ci if (IS_ERR(ctx->m2m_ctx)) { 64562306a36Sopenharmony_ci pr_err("Failed to initialize m2m context"); 64662306a36Sopenharmony_ci ret = PTR_ERR(ctx->m2m_ctx); 64762306a36Sopenharmony_ci goto error_ctrls; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (gsc->m2m.refcnt++ == 0) 65162306a36Sopenharmony_ci set_bit(ST_M2M_OPEN, &gsc->state); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci mutex_unlock(&gsc->lock); 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cierror_ctrls: 65962306a36Sopenharmony_ci gsc_ctrls_delete(ctx); 66062306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 66162306a36Sopenharmony_cierror_fh: 66262306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 66362306a36Sopenharmony_ci kfree(ctx); 66462306a36Sopenharmony_ciunlock: 66562306a36Sopenharmony_ci mutex_unlock(&gsc->lock); 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int gsc_m2m_release(struct file *file) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 67262306a36Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci pr_debug("pid: %d, state: 0x%lx, refcnt= %d", 67562306a36Sopenharmony_ci task_pid_nr(current), gsc->state, gsc->m2m.refcnt); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci mutex_lock(&gsc->lock); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->m2m_ctx); 68062306a36Sopenharmony_ci gsc_ctrls_delete(ctx); 68162306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 68262306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (--gsc->m2m.refcnt <= 0) 68562306a36Sopenharmony_ci clear_bit(ST_M2M_OPEN, &gsc->state); 68662306a36Sopenharmony_ci kfree(ctx); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci mutex_unlock(&gsc->lock); 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic __poll_t gsc_m2m_poll(struct file *file, 69362306a36Sopenharmony_ci struct poll_table_struct *wait) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 69662306a36Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 69762306a36Sopenharmony_ci __poll_t ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 70062306a36Sopenharmony_ci return EPOLLERR; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); 70362306a36Sopenharmony_ci mutex_unlock(&gsc->lock); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return ret; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct gsc_ctx *ctx = fh_to_ctx(file->private_data); 71162306a36Sopenharmony_ci struct gsc_dev *gsc = ctx->gsc_dev; 71262306a36Sopenharmony_ci int ret; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (mutex_lock_interruptible(&gsc->lock)) 71562306a36Sopenharmony_ci return -ERESTARTSYS; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); 71862306a36Sopenharmony_ci mutex_unlock(&gsc->lock); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic const struct v4l2_file_operations gsc_m2m_fops = { 72462306a36Sopenharmony_ci .owner = THIS_MODULE, 72562306a36Sopenharmony_ci .open = gsc_m2m_open, 72662306a36Sopenharmony_ci .release = gsc_m2m_release, 72762306a36Sopenharmony_ci .poll = gsc_m2m_poll, 72862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 72962306a36Sopenharmony_ci .mmap = gsc_m2m_mmap, 73062306a36Sopenharmony_ci}; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic const struct v4l2_m2m_ops gsc_m2m_ops = { 73362306a36Sopenharmony_ci .device_run = gsc_m2m_device_run, 73462306a36Sopenharmony_ci .job_abort = gsc_m2m_job_abort, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ciint gsc_register_m2m_device(struct gsc_dev *gsc) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct platform_device *pdev; 74062306a36Sopenharmony_ci int ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!gsc) 74362306a36Sopenharmony_ci return -ENODEV; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci pdev = gsc->pdev; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci gsc->vdev.fops = &gsc_m2m_fops; 74862306a36Sopenharmony_ci gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops; 74962306a36Sopenharmony_ci gsc->vdev.release = video_device_release_empty; 75062306a36Sopenharmony_ci gsc->vdev.lock = &gsc->lock; 75162306a36Sopenharmony_ci gsc->vdev.vfl_dir = VFL_DIR_M2M; 75262306a36Sopenharmony_ci gsc->vdev.v4l2_dev = &gsc->v4l2_dev; 75362306a36Sopenharmony_ci gsc->vdev.device_caps = V4L2_CAP_STREAMING | 75462306a36Sopenharmony_ci V4L2_CAP_VIDEO_M2M_MPLANE; 75562306a36Sopenharmony_ci snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", 75662306a36Sopenharmony_ci GSC_MODULE_NAME, gsc->id); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci video_set_drvdata(&gsc->vdev, gsc); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci gsc->m2m.vfd = &gsc->vdev; 76162306a36Sopenharmony_ci gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops); 76262306a36Sopenharmony_ci if (IS_ERR(gsc->m2m.m2m_dev)) { 76362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n"); 76462306a36Sopenharmony_ci return PTR_ERR(gsc->m2m.m2m_dev); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); 76862306a36Sopenharmony_ci if (ret) { 76962306a36Sopenharmony_ci dev_err(&pdev->dev, 77062306a36Sopenharmony_ci "%s(): failed to register video device\n", __func__); 77162306a36Sopenharmony_ci goto err_m2m_release; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num); 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cierr_m2m_release: 77862306a36Sopenharmony_ci v4l2_m2m_release(gsc->m2m.m2m_dev); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid gsc_unregister_m2m_device(struct gsc_dev *gsc) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci if (gsc) { 78662306a36Sopenharmony_ci v4l2_m2m_release(gsc->m2m.m2m_dev); 78762306a36Sopenharmony_ci video_unregister_device(&gsc->vdev); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci} 790