790 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.4 * http://www.samsung.com5 *6 * Samsung EXYNOS5 SoC series G-Scaler driver7 */8 9#include <linux/module.h>10#include <linux/kernel.h>11#include <linux/types.h>12#include <linux/errno.h>13#include <linux/bug.h>14#include <linux/interrupt.h>15#include <linux/workqueue.h>16#include <linux/device.h>17#include <linux/platform_device.h>18#include <linux/list.h>19#include <linux/io.h>20#include <linux/slab.h>21#include <linux/clk.h>22 23#include <media/v4l2-ioctl.h>24 25#include "gsc-core.h"26 27static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)28{29 struct gsc_ctx *curr_ctx;30 struct gsc_dev *gsc = ctx->gsc_dev;31 int ret;32 33 curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);34 if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))35 return 0;36 37 gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);38 ret = wait_event_timeout(gsc->irq_queue,39 !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),40 GSC_SHUTDOWN_TIMEOUT);41 42 return ret == 0 ? -ETIMEDOUT : ret;43}44 45static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)46{47 int ret;48 49 ret = gsc_m2m_ctx_stop_req(ctx);50 if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {51 gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);52 gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);53 }54}55 56static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)57{58 struct gsc_ctx *ctx = q->drv_priv;59 60 return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev);61}62 63static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx)64{65 struct vb2_v4l2_buffer *src_vb, *dst_vb;66 67 while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {68 src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);69 v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);70 }71 72 while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) {73 dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);74 v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);75 }76}77 78static void gsc_m2m_stop_streaming(struct vb2_queue *q)79{80 struct gsc_ctx *ctx = q->drv_priv;81 82 __gsc_m2m_job_abort(ctx);83 84 __gsc_m2m_cleanup_queue(ctx);85 86 pm_runtime_put(&ctx->gsc_dev->pdev->dev);87}88 89void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)90{91 struct vb2_v4l2_buffer *src_vb, *dst_vb;92 93 if (!ctx || !ctx->m2m_ctx)94 return;95 96 src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);97 dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);98 99 if (src_vb && dst_vb) {100 dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;101 dst_vb->timecode = src_vb->timecode;102 dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;103 dst_vb->flags |=104 src_vb->flags105 & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;106 107 v4l2_m2m_buf_done(src_vb, vb_state);108 v4l2_m2m_buf_done(dst_vb, vb_state);109 110 v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,111 ctx->m2m_ctx);112 }113}114 115static void gsc_m2m_job_abort(void *priv)116{117 __gsc_m2m_job_abort((struct gsc_ctx *)priv);118}119 120static int gsc_get_bufs(struct gsc_ctx *ctx)121{122 struct gsc_frame *s_frame, *d_frame;123 struct vb2_v4l2_buffer *src_vb, *dst_vb;124 int ret;125 126 s_frame = &ctx->s_frame;127 d_frame = &ctx->d_frame;128 129 src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);130 ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr);131 if (ret)132 return ret;133 134 dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);135 ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr);136 if (ret)137 return ret;138 139 dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;140 141 return 0;142}143 144static void gsc_m2m_device_run(void *priv)145{146 struct gsc_ctx *ctx = priv;147 struct gsc_dev *gsc;148 unsigned long flags;149 int ret;150 bool is_set = false;151 152 if (WARN(!ctx, "null hardware context\n"))153 return;154 155 gsc = ctx->gsc_dev;156 spin_lock_irqsave(&gsc->slock, flags);157 158 set_bit(ST_M2M_PEND, &gsc->state);159 160 /* Reconfigure hardware if the context has changed. */161 if (gsc->m2m.ctx != ctx) {162 pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",163 gsc->m2m.ctx, ctx);164 ctx->state |= GSC_PARAMS;165 gsc->m2m.ctx = ctx;166 }167 168 is_set = ctx->state & GSC_CTX_STOP_REQ;169 if (is_set) {170 ctx->state &= ~GSC_CTX_STOP_REQ;171 ctx->state |= GSC_CTX_ABORT;172 wake_up(&gsc->irq_queue);173 goto put_device;174 }175 176 ret = gsc_get_bufs(ctx);177 if (ret) {178 pr_err("Wrong address");179 goto put_device;180 }181 182 gsc_set_prefbuf(gsc, &ctx->s_frame);183 gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);184 gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);185 186 if (ctx->state & GSC_PARAMS) {187 gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);188 gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);189 gsc_hw_set_frm_done_irq_mask(gsc, false);190 gsc_hw_set_gsc_irq_enable(gsc, true);191 192 if (gsc_set_scaler_info(ctx)) {193 pr_err("Scaler setup error");194 goto put_device;195 }196 197 gsc_hw_set_input_path(ctx);198 gsc_hw_set_in_size(ctx);199 gsc_hw_set_in_image_format(ctx);200 201 gsc_hw_set_output_path(ctx);202 gsc_hw_set_out_size(ctx);203 gsc_hw_set_out_image_format(ctx);204 205 gsc_hw_set_prescaler(ctx);206 gsc_hw_set_mainscaler(ctx);207 gsc_hw_set_rotation(ctx);208 gsc_hw_set_global_alpha(ctx);209 }210 211 /* update shadow registers */212 gsc_hw_set_sfr_update(ctx);213 214 ctx->state &= ~GSC_PARAMS;215 gsc_hw_enable_control(gsc, true);216 217 spin_unlock_irqrestore(&gsc->slock, flags);218 return;219 220put_device:221 ctx->state &= ~GSC_PARAMS;222 spin_unlock_irqrestore(&gsc->slock, flags);223}224 225static int gsc_m2m_queue_setup(struct vb2_queue *vq,226 unsigned int *num_buffers, unsigned int *num_planes,227 unsigned int sizes[], struct device *alloc_devs[])228{229 struct gsc_ctx *ctx = vb2_get_drv_priv(vq);230 struct gsc_frame *frame;231 int i;232 233 frame = ctx_get_frame(ctx, vq->type);234 if (IS_ERR(frame))235 return PTR_ERR(frame);236 237 if (!frame->fmt)238 return -EINVAL;239 240 *num_planes = frame->fmt->num_planes;241 for (i = 0; i < frame->fmt->num_planes; i++)242 sizes[i] = frame->payload[i];243 return 0;244}245 246static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)247{248 struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);249 struct gsc_frame *frame;250 int i;251 252 frame = ctx_get_frame(ctx, vb->vb2_queue->type);253 if (IS_ERR(frame))254 return PTR_ERR(frame);255 256 if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {257 for (i = 0; i < frame->fmt->num_planes; i++)258 vb2_set_plane_payload(vb, i, frame->payload[i]);259 }260 261 return 0;262}263 264static void gsc_m2m_buf_queue(struct vb2_buffer *vb)265{266 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);267 struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);268 269 pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);270 271 if (ctx->m2m_ctx)272 v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf);273}274 275static const struct vb2_ops gsc_m2m_qops = {276 .queue_setup = gsc_m2m_queue_setup,277 .buf_prepare = gsc_m2m_buf_prepare,278 .buf_queue = gsc_m2m_buf_queue,279 .wait_prepare = vb2_ops_wait_prepare,280 .wait_finish = vb2_ops_wait_finish,281 .stop_streaming = gsc_m2m_stop_streaming,282 .start_streaming = gsc_m2m_start_streaming,283};284 285static int gsc_m2m_querycap(struct file *file, void *fh,286 struct v4l2_capability *cap)287{288 strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver));289 strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card));290 return 0;291}292 293static int gsc_m2m_enum_fmt(struct file *file, void *priv,294 struct v4l2_fmtdesc *f)295{296 return gsc_enum_fmt(f);297}298 299static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,300 struct v4l2_format *f)301{302 struct gsc_ctx *ctx = fh_to_ctx(fh);303 304 return gsc_g_fmt_mplane(ctx, f);305}306 307static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,308 struct v4l2_format *f)309{310 struct gsc_ctx *ctx = fh_to_ctx(fh);311 312 return gsc_try_fmt_mplane(ctx, f);313}314 315static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,316 struct v4l2_format *f)317{318 struct gsc_ctx *ctx = fh_to_ctx(fh);319 struct vb2_queue *vq;320 struct gsc_frame *frame;321 struct v4l2_pix_format_mplane *pix;322 int i, ret = 0;323 324 ret = gsc_m2m_try_fmt_mplane(file, fh, f);325 if (ret)326 return ret;327 328 vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);329 330 if (vb2_is_streaming(vq)) {331 pr_err("queue (%d) busy", f->type);332 return -EBUSY;333 }334 335 if (V4L2_TYPE_IS_OUTPUT(f->type))336 frame = &ctx->s_frame;337 else338 frame = &ctx->d_frame;339 340 pix = &f->fmt.pix_mp;341 frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);342 frame->colorspace = pix->colorspace;343 if (!frame->fmt)344 return -EINVAL;345 346 for (i = 0; i < frame->fmt->num_planes; i++)347 frame->payload[i] = pix->plane_fmt[i].sizeimage;348 349 gsc_set_frame_size(frame, pix->width, pix->height);350 351 if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)352 gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);353 else354 gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);355 356 pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);357 358 return 0;359}360 361static int gsc_m2m_reqbufs(struct file *file, void *fh,362 struct v4l2_requestbuffers *reqbufs)363{364 struct gsc_ctx *ctx = fh_to_ctx(fh);365 struct gsc_dev *gsc = ctx->gsc_dev;366 u32 max_cnt;367 368 max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?369 gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;370 if (reqbufs->count > max_cnt)371 return -EINVAL;372 373 return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);374}375 376static int gsc_m2m_expbuf(struct file *file, void *fh,377 struct v4l2_exportbuffer *eb)378{379 struct gsc_ctx *ctx = fh_to_ctx(fh);380 return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);381}382 383static int gsc_m2m_querybuf(struct file *file, void *fh,384 struct v4l2_buffer *buf)385{386 struct gsc_ctx *ctx = fh_to_ctx(fh);387 return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);388}389 390static int gsc_m2m_qbuf(struct file *file, void *fh,391 struct v4l2_buffer *buf)392{393 struct gsc_ctx *ctx = fh_to_ctx(fh);394 return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);395}396 397static int gsc_m2m_dqbuf(struct file *file, void *fh,398 struct v4l2_buffer *buf)399{400 struct gsc_ctx *ctx = fh_to_ctx(fh);401 return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);402}403 404static int gsc_m2m_streamon(struct file *file, void *fh,405 enum v4l2_buf_type type)406{407 struct gsc_ctx *ctx = fh_to_ctx(fh);408 409 /* The source and target color format need to be set */410 if (V4L2_TYPE_IS_OUTPUT(type)) {411 if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))412 return -EINVAL;413 } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {414 return -EINVAL;415 }416 417 return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);418}419 420static int gsc_m2m_streamoff(struct file *file, void *fh,421 enum v4l2_buf_type type)422{423 struct gsc_ctx *ctx = fh_to_ctx(fh);424 return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);425}426 427/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */428static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)429{430 if (a->left < b->left || a->top < b->top)431 return 0;432 433 if (a->left + a->width > b->left + b->width)434 return 0;435 436 if (a->top + a->height > b->top + b->height)437 return 0;438 439 return 1;440}441 442static int gsc_m2m_g_selection(struct file *file, void *fh,443 struct v4l2_selection *s)444{445 struct gsc_frame *frame;446 struct gsc_ctx *ctx = fh_to_ctx(fh);447 448 if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&449 (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))450 return -EINVAL;451 452 frame = ctx_get_frame(ctx, s->type);453 if (IS_ERR(frame))454 return PTR_ERR(frame);455 456 switch (s->target) {457 case V4L2_SEL_TGT_COMPOSE_DEFAULT:458 case V4L2_SEL_TGT_COMPOSE_BOUNDS:459 case V4L2_SEL_TGT_CROP_BOUNDS:460 case V4L2_SEL_TGT_CROP_DEFAULT:461 s->r.left = 0;462 s->r.top = 0;463 s->r.width = frame->f_width;464 s->r.height = frame->f_height;465 return 0;466 467 case V4L2_SEL_TGT_COMPOSE:468 case V4L2_SEL_TGT_CROP:469 s->r.left = frame->crop.left;470 s->r.top = frame->crop.top;471 s->r.width = frame->crop.width;472 s->r.height = frame->crop.height;473 return 0;474 }475 476 return -EINVAL;477}478 479static int gsc_m2m_s_selection(struct file *file, void *fh,480 struct v4l2_selection *s)481{482 struct gsc_frame *frame;483 struct gsc_ctx *ctx = fh_to_ctx(fh);484 struct gsc_variant *variant = ctx->gsc_dev->variant;485 struct v4l2_selection sel = *s;486 int ret;487 488 if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&489 (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))490 return -EINVAL;491 492 ret = gsc_try_selection(ctx, &sel);493 if (ret)494 return ret;495 496 if (s->flags & V4L2_SEL_FLAG_LE &&497 !is_rectangle_enclosed(&sel.r, &s->r))498 return -ERANGE;499 500 if (s->flags & V4L2_SEL_FLAG_GE &&501 !is_rectangle_enclosed(&s->r, &sel.r))502 return -ERANGE;503 504 s->r = sel.r;505 506 switch (s->target) {507 case V4L2_SEL_TGT_COMPOSE_BOUNDS:508 case V4L2_SEL_TGT_COMPOSE_DEFAULT:509 case V4L2_SEL_TGT_COMPOSE:510 frame = &ctx->s_frame;511 break;512 513 case V4L2_SEL_TGT_CROP_BOUNDS:514 case V4L2_SEL_TGT_CROP:515 case V4L2_SEL_TGT_CROP_DEFAULT:516 frame = &ctx->d_frame;517 break;518 519 default:520 return -EINVAL;521 }522 523 /* Check to see if scaling ratio is within supported range */524 if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {525 if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {526 ret = gsc_check_scaler_ratio(variant, sel.r.width,527 sel.r.height, ctx->d_frame.crop.width,528 ctx->d_frame.crop.height,529 ctx->gsc_ctrls.rotate->val, ctx->out_path);530 } else {531 ret = gsc_check_scaler_ratio(variant,532 ctx->s_frame.crop.width,533 ctx->s_frame.crop.height, sel.r.width,534 sel.r.height, ctx->gsc_ctrls.rotate->val,535 ctx->out_path);536 }537 538 if (ret) {539 pr_err("Out of scaler range");540 return -EINVAL;541 }542 }543 544 frame->crop = sel.r;545 546 gsc_ctx_state_lock_set(GSC_PARAMS, ctx);547 return 0;548}549 550static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {551 .vidioc_querycap = gsc_m2m_querycap,552 .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt,553 .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt,554 .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane,555 .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane,556 .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane,557 .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane,558 .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,559 .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,560 .vidioc_reqbufs = gsc_m2m_reqbufs,561 .vidioc_expbuf = gsc_m2m_expbuf,562 .vidioc_querybuf = gsc_m2m_querybuf,563 .vidioc_qbuf = gsc_m2m_qbuf,564 .vidioc_dqbuf = gsc_m2m_dqbuf,565 .vidioc_streamon = gsc_m2m_streamon,566 .vidioc_streamoff = gsc_m2m_streamoff,567 .vidioc_g_selection = gsc_m2m_g_selection,568 .vidioc_s_selection = gsc_m2m_s_selection569};570 571static int queue_init(void *priv, struct vb2_queue *src_vq,572 struct vb2_queue *dst_vq)573{574 struct gsc_ctx *ctx = priv;575 int ret;576 577 memset(src_vq, 0, sizeof(*src_vq));578 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;579 src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;580 src_vq->drv_priv = ctx;581 src_vq->ops = &gsc_m2m_qops;582 src_vq->mem_ops = &vb2_dma_contig_memops;583 src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);584 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;585 src_vq->lock = &ctx->gsc_dev->lock;586 src_vq->dev = &ctx->gsc_dev->pdev->dev;587 588 ret = vb2_queue_init(src_vq);589 if (ret)590 return ret;591 592 memset(dst_vq, 0, sizeof(*dst_vq));593 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;594 dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;595 dst_vq->drv_priv = ctx;596 dst_vq->ops = &gsc_m2m_qops;597 dst_vq->mem_ops = &vb2_dma_contig_memops;598 dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);599 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;600 dst_vq->lock = &ctx->gsc_dev->lock;601 dst_vq->dev = &ctx->gsc_dev->pdev->dev;602 603 return vb2_queue_init(dst_vq);604}605 606static int gsc_m2m_open(struct file *file)607{608 struct gsc_dev *gsc = video_drvdata(file);609 struct gsc_ctx *ctx = NULL;610 int ret;611 612 pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);613 614 if (mutex_lock_interruptible(&gsc->lock))615 return -ERESTARTSYS;616 617 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);618 if (!ctx) {619 ret = -ENOMEM;620 goto unlock;621 }622 623 v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);624 ret = gsc_ctrls_create(ctx);625 if (ret)626 goto error_fh;627 628 /* Use separate control handler per file handle */629 ctx->fh.ctrl_handler = &ctx->ctrl_handler;630 file->private_data = &ctx->fh;631 v4l2_fh_add(&ctx->fh);632 633 ctx->gsc_dev = gsc;634 /* Default color format */635 ctx->s_frame.fmt = get_format(0);636 ctx->d_frame.fmt = get_format(0);637 /* Setup the device context for mem2mem mode. */638 ctx->state = GSC_CTX_M2M;639 ctx->flags = 0;640 ctx->in_path = GSC_DMA;641 ctx->out_path = GSC_DMA;642 643 ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);644 if (IS_ERR(ctx->m2m_ctx)) {645 pr_err("Failed to initialize m2m context");646 ret = PTR_ERR(ctx->m2m_ctx);647 goto error_ctrls;648 }649 650 if (gsc->m2m.refcnt++ == 0)651 set_bit(ST_M2M_OPEN, &gsc->state);652 653 pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);654 655 mutex_unlock(&gsc->lock);656 return 0;657 658error_ctrls:659 gsc_ctrls_delete(ctx);660 v4l2_fh_del(&ctx->fh);661error_fh:662 v4l2_fh_exit(&ctx->fh);663 kfree(ctx);664unlock:665 mutex_unlock(&gsc->lock);666 return ret;667}668 669static int gsc_m2m_release(struct file *file)670{671 struct gsc_ctx *ctx = fh_to_ctx(file->private_data);672 struct gsc_dev *gsc = ctx->gsc_dev;673 674 pr_debug("pid: %d, state: 0x%lx, refcnt= %d",675 task_pid_nr(current), gsc->state, gsc->m2m.refcnt);676 677 mutex_lock(&gsc->lock);678 679 v4l2_m2m_ctx_release(ctx->m2m_ctx);680 gsc_ctrls_delete(ctx);681 v4l2_fh_del(&ctx->fh);682 v4l2_fh_exit(&ctx->fh);683 684 if (--gsc->m2m.refcnt <= 0)685 clear_bit(ST_M2M_OPEN, &gsc->state);686 kfree(ctx);687 688 mutex_unlock(&gsc->lock);689 return 0;690}691 692static __poll_t gsc_m2m_poll(struct file *file,693 struct poll_table_struct *wait)694{695 struct gsc_ctx *ctx = fh_to_ctx(file->private_data);696 struct gsc_dev *gsc = ctx->gsc_dev;697 __poll_t ret;698 699 if (mutex_lock_interruptible(&gsc->lock))700 return EPOLLERR;701 702 ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);703 mutex_unlock(&gsc->lock);704 705 return ret;706}707 708static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)709{710 struct gsc_ctx *ctx = fh_to_ctx(file->private_data);711 struct gsc_dev *gsc = ctx->gsc_dev;712 int ret;713 714 if (mutex_lock_interruptible(&gsc->lock))715 return -ERESTARTSYS;716 717 ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);718 mutex_unlock(&gsc->lock);719 720 return ret;721}722 723static const struct v4l2_file_operations gsc_m2m_fops = {724 .owner = THIS_MODULE,725 .open = gsc_m2m_open,726 .release = gsc_m2m_release,727 .poll = gsc_m2m_poll,728 .unlocked_ioctl = video_ioctl2,729 .mmap = gsc_m2m_mmap,730};731 732static const struct v4l2_m2m_ops gsc_m2m_ops = {733 .device_run = gsc_m2m_device_run,734 .job_abort = gsc_m2m_job_abort,735};736 737int gsc_register_m2m_device(struct gsc_dev *gsc)738{739 struct platform_device *pdev;740 int ret;741 742 if (!gsc)743 return -ENODEV;744 745 pdev = gsc->pdev;746 747 gsc->vdev.fops = &gsc_m2m_fops;748 gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops;749 gsc->vdev.release = video_device_release_empty;750 gsc->vdev.lock = &gsc->lock;751 gsc->vdev.vfl_dir = VFL_DIR_M2M;752 gsc->vdev.v4l2_dev = &gsc->v4l2_dev;753 gsc->vdev.device_caps = V4L2_CAP_STREAMING |754 V4L2_CAP_VIDEO_M2M_MPLANE;755 snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",756 GSC_MODULE_NAME, gsc->id);757 758 video_set_drvdata(&gsc->vdev, gsc);759 760 gsc->m2m.vfd = &gsc->vdev;761 gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);762 if (IS_ERR(gsc->m2m.m2m_dev)) {763 dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");764 return PTR_ERR(gsc->m2m.m2m_dev);765 }766 767 ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1);768 if (ret) {769 dev_err(&pdev->dev,770 "%s(): failed to register video device\n", __func__);771 goto err_m2m_release;772 }773 774 pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);775 return 0;776 777err_m2m_release:778 v4l2_m2m_release(gsc->m2m.m2m_dev);779 780 return ret;781}782 783void gsc_unregister_m2m_device(struct gsc_dev *gsc)784{785 if (gsc) {786 v4l2_m2m_release(gsc->m2m.m2m_dev);787 video_unregister_device(&gsc->vdev);788 }789}790