File Index
Files and directories
Sign in
FILE

mtk_mdp_m2m.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2015-2016 MediaTek Inc.
   4 * Author: Houlong Wei <houlong.wei@mediatek.com>
   5 *         Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/errno.h>
  10#include <linux/kernel.h>
  11#include <linux/pm_runtime.h>
  12#include <linux/slab.h>
  13#include <linux/workqueue.h>
  14#include <media/v4l2-event.h>
  15#include <media/v4l2-ioctl.h>
  16
  17#include "mtk_mdp_core.h"
  18#include "mtk_mdp_m2m.h"
  19#include "mtk_mdp_regs.h"
  20#include "mtk_vpu.h"
  21
  22
  23/**
  24 *  struct mtk_mdp_pix_limit - image pixel size limits
  25 *  @org_w: source pixel width
  26 *  @org_h: source pixel height
  27 *  @target_rot_dis_w: pixel dst scaled width with the rotator is off
  28 *  @target_rot_dis_h: pixel dst scaled height with the rotator is off
  29 *  @target_rot_en_w: pixel dst scaled width with the rotator is on
  30 *  @target_rot_en_h: pixel dst scaled height with the rotator is on
  31 */
  32struct mtk_mdp_pix_limit {
  33	u16 org_w;
  34	u16 org_h;
  35	u16 target_rot_dis_w;
  36	u16 target_rot_dis_h;
  37	u16 target_rot_en_w;
  38	u16 target_rot_en_h;
  39};
  40
  41static struct mtk_mdp_pix_align mtk_mdp_size_align = {
  42	.org_w			= 16,
  43	.org_h			= 16,
  44	.target_w		= 2,
  45	.target_h		= 2,
  46};
  47
  48static const struct mtk_mdp_fmt mtk_mdp_formats[] = {
  49	{
  50		.pixelformat	= V4L2_PIX_FMT_MT21C,
  51		.depth		= { 8, 4 },
  52		.row_depth	= { 8, 8 },
  53		.num_planes	= 2,
  54		.num_comp	= 2,
  55		.align		= &mtk_mdp_size_align,
  56		.flags		= MTK_MDP_FMT_FLAG_OUTPUT,
  57	}, {
  58		.pixelformat	= V4L2_PIX_FMT_NV12M,
  59		.depth		= { 8, 4 },
  60		.row_depth	= { 8, 8 },
  61		.num_planes	= 2,
  62		.num_comp	= 2,
  63		.flags		= MTK_MDP_FMT_FLAG_OUTPUT |
  64				  MTK_MDP_FMT_FLAG_CAPTURE,
  65	}, {
  66		.pixelformat	= V4L2_PIX_FMT_YUV420M,
  67		.depth		= { 8, 2, 2 },
  68		.row_depth	= { 8, 4, 4 },
  69		.num_planes	= 3,
  70		.num_comp	= 3,
  71		.flags		= MTK_MDP_FMT_FLAG_OUTPUT |
  72				  MTK_MDP_FMT_FLAG_CAPTURE,
  73	}, {
  74		.pixelformat	= V4L2_PIX_FMT_YVU420,
  75		.depth		= { 12 },
  76		.row_depth	= { 8 },
  77		.num_planes	= 1,
  78		.num_comp	= 3,
  79		.flags		= MTK_MDP_FMT_FLAG_OUTPUT |
  80				  MTK_MDP_FMT_FLAG_CAPTURE,
  81	}
  82};
  83
  84static struct mtk_mdp_pix_limit mtk_mdp_size_max = {
  85	.target_rot_dis_w	= 4096,
  86	.target_rot_dis_h	= 4096,
  87	.target_rot_en_w	= 4096,
  88	.target_rot_en_h	= 4096,
  89};
  90
  91static struct mtk_mdp_pix_limit mtk_mdp_size_min = {
  92	.org_w			= 16,
  93	.org_h			= 16,
  94	.target_rot_dis_w	= 16,
  95	.target_rot_dis_h	= 16,
  96	.target_rot_en_w	= 16,
  97	.target_rot_en_h	= 16,
  98};
  99
 100/* align size for normal raster scan pixel format */
 101static struct mtk_mdp_pix_align mtk_mdp_rs_align = {
 102	.org_w			= 2,
 103	.org_h			= 2,
 104	.target_w		= 2,
 105	.target_h		= 2,
 106};
 107
 108static struct mtk_mdp_variant mtk_mdp_default_variant = {
 109	.pix_max		= &mtk_mdp_size_max,
 110	.pix_min		= &mtk_mdp_size_min,
 111	.pix_align		= &mtk_mdp_rs_align,
 112	.h_scale_up_max		= 32,
 113	.v_scale_up_max		= 32,
 114	.h_scale_down_max	= 32,
 115	.v_scale_down_max	= 128,
 116};
 117
 118static const struct mtk_mdp_fmt *mtk_mdp_find_fmt(u32 pixelformat, u32 type)
 119{
 120	u32 i, flag;
 121
 122	flag = V4L2_TYPE_IS_OUTPUT(type) ? MTK_MDP_FMT_FLAG_OUTPUT :
 123					   MTK_MDP_FMT_FLAG_CAPTURE;
 124
 125	for (i = 0; i < ARRAY_SIZE(mtk_mdp_formats); ++i) {
 126		if (!(mtk_mdp_formats[i].flags & flag))
 127			continue;
 128		if (mtk_mdp_formats[i].pixelformat == pixelformat)
 129			return &mtk_mdp_formats[i];
 130	}
 131	return NULL;
 132}
 133
 134static const struct mtk_mdp_fmt *mtk_mdp_find_fmt_by_index(u32 index, u32 type)
 135{
 136	u32 i, flag, num = 0;
 137
 138	flag = V4L2_TYPE_IS_OUTPUT(type) ? MTK_MDP_FMT_FLAG_OUTPUT :
 139					   MTK_MDP_FMT_FLAG_CAPTURE;
 140
 141	for (i = 0; i < ARRAY_SIZE(mtk_mdp_formats); ++i) {
 142		if (!(mtk_mdp_formats[i].flags & flag))
 143			continue;
 144		if (index == num)
 145			return &mtk_mdp_formats[i];
 146		num++;
 147	}
 148	return NULL;
 149}
 150
 151static void mtk_mdp_bound_align_image(u32 *w, unsigned int wmin,
 152				      unsigned int wmax, unsigned int align_w,
 153				      u32 *h, unsigned int hmin,
 154				      unsigned int hmax, unsigned int align_h)
 155{
 156	int org_w, org_h, step_w, step_h;
 157	int walign, halign;
 158
 159	org_w = *w;
 160	org_h = *h;
 161	walign = ffs(align_w) - 1;
 162	halign = ffs(align_h) - 1;
 163	v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
 164
 165	step_w = 1 << walign;
 166	step_h = 1 << halign;
 167	if (*w < org_w && (*w + step_w) <= wmax)
 168		*w += step_w;
 169	if (*h < org_h && (*h + step_h) <= hmax)
 170		*h += step_h;
 171}
 172
 173static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx,
 174							struct v4l2_format *f)
 175{
 176	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
 177	struct mtk_mdp_variant *variant = mdp->variant;
 178	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
 179	const struct mtk_mdp_fmt *fmt;
 180	u32 max_w, max_h, align_w, align_h;
 181	u32 min_w, min_h, org_w, org_h;
 182	int i;
 183
 184	fmt = mtk_mdp_find_fmt(pix_mp->pixelformat, f->type);
 185	if (!fmt)
 186		fmt = mtk_mdp_find_fmt_by_index(0, f->type);
 187	if (!fmt) {
 188		dev_dbg(&ctx->mdp_dev->pdev->dev,
 189			"pixelformat format 0x%X invalid\n",
 190			pix_mp->pixelformat);
 191		return NULL;
 192	}
 193
 194	pix_mp->field = V4L2_FIELD_NONE;
 195	pix_mp->pixelformat = fmt->pixelformat;
 196	if (V4L2_TYPE_IS_CAPTURE(f->type)) {
 197		pix_mp->colorspace = ctx->colorspace;
 198		pix_mp->xfer_func = ctx->xfer_func;
 199		pix_mp->ycbcr_enc = ctx->ycbcr_enc;
 200		pix_mp->quantization = ctx->quant;
 201	}
 202
 203	max_w = variant->pix_max->target_rot_dis_w;
 204	max_h = variant->pix_max->target_rot_dis_h;
 205
 206	if (fmt->align == NULL) {
 207		/* use default alignment */
 208		align_w = variant->pix_align->org_w;
 209		align_h = variant->pix_align->org_h;
 210	} else {
 211		align_w = fmt->align->org_w;
 212		align_h = fmt->align->org_h;
 213	}
 214
 215	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
 216		min_w = variant->pix_min->org_w;
 217		min_h = variant->pix_min->org_h;
 218	} else {
 219		min_w = variant->pix_min->target_rot_dis_w;
 220		min_h = variant->pix_min->target_rot_dis_h;
 221	}
 222
 223	mtk_mdp_dbg(2, "[%d] type:%d, wxh:%ux%u, align:%ux%u, max:%ux%u",
 224		    ctx->id, f->type, pix_mp->width, pix_mp->height,
 225		    align_w, align_h, max_w, max_h);
 226	/*
 227	 * To check if image size is modified to adjust parameter against
 228	 * hardware abilities
 229	 */
 230	org_w = pix_mp->width;
 231	org_h = pix_mp->height;
 232
 233	mtk_mdp_bound_align_image(&pix_mp->width, min_w, max_w, align_w,
 234				  &pix_mp->height, min_h, max_h, align_h);
 235
 236	if (org_w != pix_mp->width || org_h != pix_mp->height)
 237		mtk_mdp_dbg(1, "[%d] size change:%ux%u to %ux%u", ctx->id,
 238			    org_w, org_h, pix_mp->width, pix_mp->height);
 239	pix_mp->num_planes = fmt->num_planes;
 240
 241	for (i = 0; i < pix_mp->num_planes; ++i) {
 242		int bpl = (pix_mp->width * fmt->row_depth[i]) / 8;
 243		int sizeimage = (pix_mp->width * pix_mp->height *
 244			fmt->depth[i]) / 8;
 245
 246		pix_mp->plane_fmt[i].bytesperline = bpl;
 247		if (pix_mp->plane_fmt[i].sizeimage < sizeimage)
 248			pix_mp->plane_fmt[i].sizeimage = sizeimage;
 249		mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)", ctx->id,
 250			    i, bpl, pix_mp->plane_fmt[i].sizeimage, sizeimage);
 251	}
 252
 253	return fmt;
 254}
 255
 256static struct mtk_mdp_frame *mtk_mdp_ctx_get_frame(struct mtk_mdp_ctx *ctx,
 257					    enum v4l2_buf_type type)
 258{
 259	if (V4L2_TYPE_IS_OUTPUT(type))
 260		return &ctx->s_frame;
 261	return &ctx->d_frame;
 262}
 263
 264static void mtk_mdp_check_crop_change(u32 new_w, u32 new_h, u32 *w, u32 *h)
 265{
 266	if (new_w != *w || new_h != *h) {
 267		mtk_mdp_dbg(1, "size change:%dx%d to %dx%d",
 268			    *w, *h, new_w, new_h);
 269
 270		*w = new_w;
 271		*h = new_h;
 272	}
 273}
 274
 275static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type,
 276			    struct v4l2_rect *r)
 277{
 278	struct mtk_mdp_frame *frame;
 279	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
 280	struct mtk_mdp_variant *variant = mdp->variant;
 281	u32 align_w, align_h, new_w, new_h;
 282	u32 min_w, min_h, max_w, max_h;
 283
 284	if (r->top < 0 || r->left < 0) {
 285		dev_err(&ctx->mdp_dev->pdev->dev,
 286			"doesn't support negative values for top & left\n");
 287		return -EINVAL;
 288	}
 289
 290	mtk_mdp_dbg(2, "[%d] type:%d, set wxh:%dx%d", ctx->id, type,
 291		    r->width, r->height);
 292
 293	frame = mtk_mdp_ctx_get_frame(ctx, type);
 294	max_w = frame->width;
 295	max_h = frame->height;
 296	new_w = r->width;
 297	new_h = r->height;
 298
 299	if (V4L2_TYPE_IS_OUTPUT(type)) {
 300		align_w = 1;
 301		align_h = 1;
 302		min_w = 64;
 303		min_h = 32;
 304	} else {
 305		align_w = variant->pix_align->target_w;
 306		align_h = variant->pix_align->target_h;
 307		if (ctx->ctrls.rotate->val == 90 ||
 308		    ctx->ctrls.rotate->val == 270) {
 309			max_w = frame->height;
 310			max_h = frame->width;
 311			min_w = variant->pix_min->target_rot_en_w;
 312			min_h = variant->pix_min->target_rot_en_h;
 313			new_w = r->height;
 314			new_h = r->width;
 315		} else {
 316			min_w = variant->pix_min->target_rot_dis_w;
 317			min_h = variant->pix_min->target_rot_dis_h;
 318		}
 319	}
 320
 321	mtk_mdp_dbg(2, "[%d] align:%dx%d, min:%dx%d, new:%dx%d", ctx->id,
 322		    align_w, align_h, min_w, min_h, new_w, new_h);
 323
 324	mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w,
 325				  &new_h, min_h, max_h, align_h);
 326
 327	if (V4L2_TYPE_IS_CAPTURE(type) &&
 328	    (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270))
 329		mtk_mdp_check_crop_change(new_h, new_w,
 330					  &r->width, &r->height);
 331	else
 332		mtk_mdp_check_crop_change(new_w, new_h,
 333					  &r->width, &r->height);
 334
 335	/* adjust left/top if cropping rectangle is out of bounds */
 336	/* Need to add code to algin left value with 2's multiple */
 337	if (r->left + new_w > max_w)
 338		r->left = max_w - new_w;
 339	if (r->top + new_h > max_h)
 340		r->top = max_h - new_h;
 341
 342	if (r->left & 1)
 343		r->left -= 1;
 344
 345	mtk_mdp_dbg(2, "[%d] crop (%d,%d)/%ux%u, max:%dx%d", ctx->id,
 346		    r->left, r->top, r->width,
 347		    r->height, max_w, max_h);
 348	return 0;
 349}
 350
 351static inline struct mtk_mdp_ctx *file_to_ctx(struct file *filp)
 352{
 353	return container_of(file_to_v4l2_fh(filp), struct mtk_mdp_ctx, fh);
 354}
 355
 356static inline struct mtk_mdp_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
 357{
 358	return container_of(ctrl->handler, struct mtk_mdp_ctx, ctrl_handler);
 359}
 360
 361void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state)
 362{
 363	mutex_lock(&ctx->slock);
 364	ctx->state |= state;
 365	mutex_unlock(&ctx->slock);
 366}
 367
 368static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask)
 369{
 370	bool ret;
 371
 372	mutex_lock(&ctx->slock);
 373	ret = (ctx->state & mask) == mask;
 374	mutex_unlock(&ctx->slock);
 375	return ret;
 376}
 377
 378static void mtk_mdp_set_frame_size(struct mtk_mdp_frame *frame, int width,
 379				   int height)
 380{
 381	frame->width = width;
 382	frame->height = height;
 383	frame->crop.width = width;
 384	frame->crop.height = height;
 385	frame->crop.left = 0;
 386	frame->crop.top = 0;
 387}
 388
 389static int mtk_mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
 390{
 391	struct mtk_mdp_ctx *ctx = q->drv_priv;
 392	int ret;
 393
 394	ret = pm_runtime_resume_and_get(&ctx->mdp_dev->pdev->dev);
 395	if (ret < 0)
 396		mtk_mdp_dbg(1, "[%d] pm_runtime_resume_and_get failed:%d",
 397			    ctx->id, ret);
 398
 399	return ret;
 400}
 401
 402static void *mtk_mdp_m2m_buf_remove(struct mtk_mdp_ctx *ctx,
 403				    enum v4l2_buf_type type)
 404{
 405	if (V4L2_TYPE_IS_OUTPUT(type))
 406		return v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
 407	else
 408		return v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
 409}
 410
 411static void mtk_mdp_m2m_stop_streaming(struct vb2_queue *q)
 412{
 413	struct mtk_mdp_ctx *ctx = q->drv_priv;
 414	struct vb2_buffer *vb;
 415
 416	vb = mtk_mdp_m2m_buf_remove(ctx, q->type);
 417	while (vb != NULL) {
 418		v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR);
 419		vb = mtk_mdp_m2m_buf_remove(ctx, q->type);
 420	}
 421
 422	pm_runtime_put(&ctx->mdp_dev->pdev->dev);
 423}
 424
 425/* The color format (num_planes) must be already configured. */
 426static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx,
 427				 struct vb2_buffer *vb,
 428				 struct mtk_mdp_frame *frame,
 429				 struct mtk_mdp_addr *addr)
 430{
 431	u32 pix_size, planes, i;
 432
 433	pix_size = frame->width * frame->height;
 434	planes = min_t(u32, frame->fmt->num_planes, ARRAY_SIZE(addr->addr));
 435	for (i = 0; i < planes; i++)
 436		addr->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
 437
 438	if (planes == 1) {
 439		if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {
 440			addr->addr[1] = (dma_addr_t)(addr->addr[0] + pix_size);
 441			addr->addr[2] = (dma_addr_t)(addr->addr[1] +
 442					(pix_size >> 2));
 443		} else {
 444			dev_err(&ctx->mdp_dev->pdev->dev,
 445				"Invalid pixelformat:0x%x\n",
 446				frame->fmt->pixelformat);
 447		}
 448	}
 449	mtk_mdp_dbg(3, "[%d] planes:%d, size:%d, addr:%p,%p,%p",
 450		    ctx->id, planes, pix_size, (void *)addr->addr[0],
 451		    (void *)addr->addr[1], (void *)addr->addr[2]);
 452}
 453
 454static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx *ctx)
 455{
 456	struct mtk_mdp_frame *s_frame, *d_frame;
 457	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
 458
 459	s_frame = &ctx->s_frame;
 460	d_frame = &ctx->d_frame;
 461
 462	src_vbuf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
 463	mtk_mdp_prepare_addr(ctx, &src_vbuf->vb2_buf, s_frame, &s_frame->addr);
 464
 465	dst_vbuf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
 466	mtk_mdp_prepare_addr(ctx, &dst_vbuf->vb2_buf, d_frame, &d_frame->addr);
 467
 468	dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp;
 469}
 470
 471static void mtk_mdp_process_done(void *priv, int vb_state)
 472{
 473	struct mtk_mdp_dev *mdp = priv;
 474	struct mtk_mdp_ctx *ctx;
 475	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
 476
 477	ctx = v4l2_m2m_get_curr_priv(mdp->m2m_dev);
 478	if (!ctx)
 479		return;
 480
 481	src_vbuf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
 482	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
 483
 484	dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp;
 485	dst_vbuf->timecode = src_vbuf->timecode;
 486	dst_vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
 487	dst_vbuf->flags |= src_vbuf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
 488
 489	v4l2_m2m_buf_done(src_vbuf, vb_state);
 490	v4l2_m2m_buf_done(dst_vbuf, vb_state);
 491	v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
 492}
 493
 494static void mtk_mdp_m2m_worker(struct work_struct *work)
 495{
 496	struct mtk_mdp_ctx *ctx =
 497				container_of(work, struct mtk_mdp_ctx, work);
 498	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
 499	enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
 500	int ret;
 501
 502	if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_CTX_ERROR)) {
 503		dev_err(&mdp->pdev->dev, "ctx is in error state");
 504		goto worker_end;
 505	}
 506
 507	mtk_mdp_m2m_get_bufs(ctx);
 508
 509	mtk_mdp_hw_set_input_addr(ctx, &ctx->s_frame.addr);
 510	mtk_mdp_hw_set_output_addr(ctx, &ctx->d_frame.addr);
 511
 512	mtk_mdp_hw_set_in_size(ctx);
 513	mtk_mdp_hw_set_in_image_format(ctx);
 514
 515	mtk_mdp_hw_set_out_size(ctx);
 516	mtk_mdp_hw_set_out_image_format(ctx);
 517
 518	mtk_mdp_hw_set_rotation(ctx);
 519	mtk_mdp_hw_set_global_alpha(ctx);
 520
 521	ret = mtk_mdp_vpu_process(&ctx->vpu);
 522	if (ret) {
 523		dev_err(&mdp->pdev->dev, "processing failed: %d", ret);
 524		goto worker_end;
 525	}
 526
 527	buf_state = VB2_BUF_STATE_DONE;
 528
 529worker_end:
 530	mtk_mdp_process_done(mdp, buf_state);
 531}
 532
 533static void mtk_mdp_m2m_device_run(void *priv)
 534{
 535	struct mtk_mdp_ctx *ctx = priv;
 536
 537	queue_work(ctx->mdp_dev->job_wq, &ctx->work);
 538}
 539
 540static int mtk_mdp_m2m_queue_setup(struct vb2_queue *vq,
 541			unsigned int *num_buffers, unsigned int *num_planes,
 542			unsigned int sizes[], struct device *alloc_devs[])
 543{
 544	struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vq);
 545	struct mtk_mdp_frame *frame;
 546	int i;
 547
 548	frame = mtk_mdp_ctx_get_frame(ctx, vq->type);
 549	*num_planes = frame->fmt->num_planes;
 550	for (i = 0; i < frame->fmt->num_planes; i++)
 551		sizes[i] = frame->payload[i];
 552	mtk_mdp_dbg(2, "[%d] type:%d, planes:%d, buffers:%d, size:%u,%u",
 553		    ctx->id, vq->type, *num_planes, *num_buffers,
 554		    sizes[0], sizes[1]);
 555	return 0;
 556}
 557
 558static int mtk_mdp_m2m_buf_prepare(struct vb2_buffer *vb)
 559{
 560	struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 561	struct mtk_mdp_frame *frame;
 562	int i;
 563
 564	frame = mtk_mdp_ctx_get_frame(ctx, vb->vb2_queue->type);
 565
 566	if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
 567		for (i = 0; i < frame->fmt->num_planes; i++)
 568			vb2_set_plane_payload(vb, i, frame->payload[i]);
 569	}
 570
 571	return 0;
 572}
 573
 574static void mtk_mdp_m2m_buf_queue(struct vb2_buffer *vb)
 575{
 576	struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 577
 578	v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
 579}
 580
 581static const struct vb2_ops mtk_mdp_m2m_qops = {
 582	.queue_setup	 = mtk_mdp_m2m_queue_setup,
 583	.buf_prepare	 = mtk_mdp_m2m_buf_prepare,
 584	.buf_queue	 = mtk_mdp_m2m_buf_queue,
 585	.stop_streaming	 = mtk_mdp_m2m_stop_streaming,
 586	.start_streaming = mtk_mdp_m2m_start_streaming,
 587};
 588
 589static int mtk_mdp_m2m_querycap(struct file *file, void *fh,
 590				struct v4l2_capability *cap)
 591{
 592	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 593	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
 594
 595	strscpy(cap->driver, MTK_MDP_MODULE_NAME, sizeof(cap->driver));
 596	strscpy(cap->card, mdp->pdev->name, sizeof(cap->card));
 597	strscpy(cap->bus_info, "platform:mt8173", sizeof(cap->bus_info));
 598
 599	return 0;
 600}
 601
 602static int mtk_mdp_enum_fmt(struct v4l2_fmtdesc *f, u32 type)
 603{
 604	const struct mtk_mdp_fmt *fmt;
 605
 606	fmt = mtk_mdp_find_fmt_by_index(f->index, type);
 607	if (!fmt)
 608		return -EINVAL;
 609
 610	f->pixelformat = fmt->pixelformat;
 611
 612	return 0;
 613}
 614
 615static int mtk_mdp_m2m_enum_fmt_vid_cap(struct file *file, void *priv,
 616					struct v4l2_fmtdesc *f)
 617{
 618	return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
 619}
 620
 621static int mtk_mdp_m2m_enum_fmt_vid_out(struct file *file, void *priv,
 622					struct v4l2_fmtdesc *f)
 623{
 624	return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
 625}
 626
 627static int mtk_mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
 628				    struct v4l2_format *f)
 629{
 630	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 631	struct mtk_mdp_frame *frame;
 632	struct v4l2_pix_format_mplane *pix_mp;
 633	int i;
 634
 635	mtk_mdp_dbg(2, "[%d] type:%d", ctx->id, f->type);
 636
 637	frame = mtk_mdp_ctx_get_frame(ctx, f->type);
 638	pix_mp = &f->fmt.pix_mp;
 639
 640	pix_mp->width = frame->width;
 641	pix_mp->height = frame->height;
 642	pix_mp->field = V4L2_FIELD_NONE;
 643	pix_mp->pixelformat = frame->fmt->pixelformat;
 644	pix_mp->num_planes = frame->fmt->num_planes;
 645	pix_mp->colorspace = ctx->colorspace;
 646	pix_mp->xfer_func = ctx->xfer_func;
 647	pix_mp->ycbcr_enc = ctx->ycbcr_enc;
 648	pix_mp->quantization = ctx->quant;
 649	mtk_mdp_dbg(2, "[%d] wxh:%dx%d", ctx->id,
 650		    pix_mp->width, pix_mp->height);
 651
 652	for (i = 0; i < pix_mp->num_planes; ++i) {
 653		pix_mp->plane_fmt[i].bytesperline = (frame->width *
 654			frame->fmt->row_depth[i]) / 8;
 655		pix_mp->plane_fmt[i].sizeimage = (frame->width *
 656			frame->height * frame->fmt->depth[i]) / 8;
 657
 658		mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%d", ctx->id, i,
 659			    pix_mp->plane_fmt[i].bytesperline,
 660			    pix_mp->plane_fmt[i].sizeimage);
 661	}
 662
 663	return 0;
 664}
 665
 666static int mtk_mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
 667				      struct v4l2_format *f)
 668{
 669	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 670
 671	if (!mtk_mdp_try_fmt_mplane(ctx, f))
 672		return -EINVAL;
 673	return 0;
 674}
 675
 676static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
 677				    struct v4l2_format *f)
 678{
 679	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 680	struct vb2_queue *vq;
 681	struct mtk_mdp_frame *frame;
 682	struct v4l2_pix_format_mplane *pix_mp;
 683	const struct mtk_mdp_fmt *fmt;
 684	int i;
 685
 686	mtk_mdp_dbg(2, "[%d] type:%d", ctx->id, f->type);
 687
 688	frame = mtk_mdp_ctx_get_frame(ctx, f->type);
 689	fmt = mtk_mdp_try_fmt_mplane(ctx, f);
 690	if (!fmt) {
 691		mtk_mdp_err("[%d] try_fmt failed, type:%d", ctx->id, f->type);
 692		return -EINVAL;
 693	}
 694	frame->fmt = fmt;
 695
 696	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
 697	if (vb2_is_streaming(vq)) {
 698		dev_info(&ctx->mdp_dev->pdev->dev, "queue %d busy", f->type);
 699		return -EBUSY;
 700	}
 701
 702	pix_mp = &f->fmt.pix_mp;
 703	for (i = 0; i < frame->fmt->num_planes; i++) {
 704		frame->payload[i] = pix_mp->plane_fmt[i].sizeimage;
 705		frame->pitch[i] = pix_mp->plane_fmt[i].bytesperline;
 706	}
 707
 708	mtk_mdp_set_frame_size(frame, pix_mp->width, pix_mp->height);
 709	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
 710		ctx->colorspace = pix_mp->colorspace;
 711		ctx->xfer_func = pix_mp->xfer_func;
 712		ctx->ycbcr_enc = pix_mp->ycbcr_enc;
 713		ctx->quant = pix_mp->quantization;
 714	}
 715
 716	mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type,
 717		    frame->width, frame->height);
 718
 719	return 0;
 720}
 721
 722static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh,
 723			       struct v4l2_requestbuffers *reqbufs)
 724{
 725	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 726
 727	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
 728}
 729
 730static int mtk_mdp_m2m_streamon(struct file *file, void *fh,
 731				enum v4l2_buf_type type)
 732{
 733	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 734	int ret;
 735
 736	if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) {
 737		ret = mtk_mdp_vpu_init(&ctx->vpu);
 738		if (ret < 0) {
 739			dev_err(&ctx->mdp_dev->pdev->dev,
 740				"vpu init failed %d\n",
 741				ret);
 742			return -EINVAL;
 743		}
 744		mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_VPU_INIT);
 745	}
 746
 747	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
 748}
 749
 750static inline bool mtk_mdp_is_target_compose(u32 target)
 751{
 752	if (target == V4L2_SEL_TGT_COMPOSE_DEFAULT
 753	    || target == V4L2_SEL_TGT_COMPOSE_BOUNDS
 754	    || target == V4L2_SEL_TGT_COMPOSE)
 755		return true;
 756	return false;
 757}
 758
 759static inline bool mtk_mdp_is_target_crop(u32 target)
 760{
 761	if (target == V4L2_SEL_TGT_CROP_DEFAULT
 762	    || target == V4L2_SEL_TGT_CROP_BOUNDS
 763	    || target == V4L2_SEL_TGT_CROP)
 764		return true;
 765	return false;
 766}
 767
 768static int mtk_mdp_m2m_g_selection(struct file *file, void *fh,
 769				       struct v4l2_selection *s)
 770{
 771	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 772	struct mtk_mdp_frame *frame;
 773	bool valid = false;
 774
 775	if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 776		if (mtk_mdp_is_target_compose(s->target))
 777			valid = true;
 778	} else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 779		if (mtk_mdp_is_target_crop(s->target))
 780			valid = true;
 781	}
 782	if (!valid) {
 783		mtk_mdp_dbg(1, "[%d] invalid type:%d,%u", ctx->id, s->type,
 784			    s->target);
 785		return -EINVAL;
 786	}
 787
 788	frame = mtk_mdp_ctx_get_frame(ctx, s->type);
 789
 790	switch (s->target) {
 791	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
 792	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
 793	case V4L2_SEL_TGT_CROP_BOUNDS:
 794	case V4L2_SEL_TGT_CROP_DEFAULT:
 795		s->r.left = 0;
 796		s->r.top = 0;
 797		s->r.width = frame->width;
 798		s->r.height = frame->height;
 799		return 0;
 800
 801	case V4L2_SEL_TGT_COMPOSE:
 802	case V4L2_SEL_TGT_CROP:
 803		s->r.left = frame->crop.left;
 804		s->r.top = frame->crop.top;
 805		s->r.width = frame->crop.width;
 806		s->r.height = frame->crop.height;
 807		return 0;
 808	}
 809
 810	return -EINVAL;
 811}
 812
 813static int mtk_mdp_check_scaler_ratio(struct mtk_mdp_variant *var, int src_w,
 814				      int src_h, int dst_w, int dst_h, int rot)
 815{
 816	int tmp_w, tmp_h;
 817
 818	if (rot == 90 || rot == 270) {
 819		tmp_w = dst_h;
 820		tmp_h = dst_w;
 821	} else {
 822		tmp_w = dst_w;
 823		tmp_h = dst_h;
 824	}
 825
 826	if ((src_w / tmp_w) > var->h_scale_down_max ||
 827	    (src_h / tmp_h) > var->v_scale_down_max ||
 828	    (tmp_w / src_w) > var->h_scale_up_max ||
 829	    (tmp_h / src_h) > var->v_scale_up_max)
 830		return -EINVAL;
 831
 832	return 0;
 833}
 834
 835static int mtk_mdp_m2m_s_selection(struct file *file, void *fh,
 836				   struct v4l2_selection *s)
 837{
 838	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
 839	struct mtk_mdp_frame *frame;
 840	struct v4l2_rect new_r;
 841	struct mtk_mdp_variant *variant = ctx->mdp_dev->variant;
 842	int ret;
 843	bool valid = false;
 844
 845	if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 846		if (s->target == V4L2_SEL_TGT_COMPOSE)
 847			valid = true;
 848	} else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 849		if (s->target == V4L2_SEL_TGT_CROP)
 850			valid = true;
 851	}
 852	if (!valid) {
 853		mtk_mdp_dbg(1, "[%d] invalid type:%d,%u", ctx->id, s->type,
 854			    s->target);
 855		return -EINVAL;
 856	}
 857
 858	new_r = s->r;
 859	ret = mtk_mdp_try_crop(ctx, s->type, &new_r);
 860	if (ret)
 861		return ret;
 862
 863	if (mtk_mdp_is_target_crop(s->target))
 864		frame = &ctx->s_frame;
 865	else
 866		frame = &ctx->d_frame;
 867
 868	/* Check to see if scaling ratio is within supported range */
 869	if (V4L2_TYPE_IS_OUTPUT(s->type))
 870		ret = mtk_mdp_check_scaler_ratio(variant, new_r.width,
 871			new_r.height, ctx->d_frame.crop.width,
 872			ctx->d_frame.crop.height,
 873			ctx->ctrls.rotate->val);
 874	else
 875		ret = mtk_mdp_check_scaler_ratio(variant,
 876			ctx->s_frame.crop.width,
 877			ctx->s_frame.crop.height, new_r.width,
 878			new_r.height, ctx->ctrls.rotate->val);
 879
 880	if (ret) {
 881		dev_info(&ctx->mdp_dev->pdev->dev,
 882			"Out of scaler range");
 883		return -EINVAL;
 884	}
 885
 886	s->r = new_r;
 887	frame->crop = new_r;
 888
 889	return 0;
 890}
 891
 892static const struct v4l2_ioctl_ops mtk_mdp_m2m_ioctl_ops = {
 893	.vidioc_querycap		= mtk_mdp_m2m_querycap,
 894	.vidioc_enum_fmt_vid_cap	= mtk_mdp_m2m_enum_fmt_vid_cap,
 895	.vidioc_enum_fmt_vid_out	= mtk_mdp_m2m_enum_fmt_vid_out,
 896	.vidioc_g_fmt_vid_cap_mplane	= mtk_mdp_m2m_g_fmt_mplane,
 897	.vidioc_g_fmt_vid_out_mplane	= mtk_mdp_m2m_g_fmt_mplane,
 898	.vidioc_try_fmt_vid_cap_mplane	= mtk_mdp_m2m_try_fmt_mplane,
 899	.vidioc_try_fmt_vid_out_mplane	= mtk_mdp_m2m_try_fmt_mplane,
 900	.vidioc_s_fmt_vid_cap_mplane	= mtk_mdp_m2m_s_fmt_mplane,
 901	.vidioc_s_fmt_vid_out_mplane	= mtk_mdp_m2m_s_fmt_mplane,
 902	.vidioc_reqbufs			= mtk_mdp_m2m_reqbufs,
 903	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
 904	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
 905	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
 906	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 907	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
 908	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
 909	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
 910	.vidioc_streamon		= mtk_mdp_m2m_streamon,
 911	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
 912	.vidioc_g_selection		= mtk_mdp_m2m_g_selection,
 913	.vidioc_s_selection		= mtk_mdp_m2m_s_selection
 914};
 915
 916static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
 917				  struct vb2_queue *dst_vq)
 918{
 919	struct mtk_mdp_ctx *ctx = priv;
 920	int ret;
 921
 922	memset(src_vq, 0, sizeof(*src_vq));
 923	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 924	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
 925	src_vq->drv_priv = ctx;
 926	src_vq->ops = &mtk_mdp_m2m_qops;
 927	src_vq->mem_ops = &vb2_dma_contig_memops;
 928	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 929	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 930	src_vq->dev = &ctx->mdp_dev->pdev->dev;
 931	src_vq->lock = &ctx->mdp_dev->lock;
 932
 933	ret = vb2_queue_init(src_vq);
 934	if (ret)
 935		return ret;
 936
 937	memset(dst_vq, 0, sizeof(*dst_vq));
 938	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 939	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
 940	dst_vq->drv_priv = ctx;
 941	dst_vq->ops = &mtk_mdp_m2m_qops;
 942	dst_vq->mem_ops = &vb2_dma_contig_memops;
 943	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 944	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 945	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
 946	dst_vq->lock = &ctx->mdp_dev->lock;
 947
 948	return vb2_queue_init(dst_vq);
 949}
 950
 951static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl)
 952{
 953	struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl);
 954	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
 955	struct mtk_mdp_variant *variant = mdp->variant;
 956	int ret = 0;
 957
 958	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
 959		return 0;
 960
 961	switch (ctrl->id) {
 962	case V4L2_CID_HFLIP:
 963		ctx->hflip = ctrl->val;
 964		break;
 965	case V4L2_CID_VFLIP:
 966		ctx->vflip = ctrl->val;
 967		break;
 968	case V4L2_CID_ROTATE:
 969		ret = mtk_mdp_check_scaler_ratio(variant,
 970				ctx->s_frame.crop.width,
 971				ctx->s_frame.crop.height,
 972				ctx->d_frame.crop.width,
 973				ctx->d_frame.crop.height,
 974				ctx->ctrls.rotate->val);
 975
 976		if (ret)
 977			return -EINVAL;
 978
 979		ctx->rotation = ctrl->val;
 980		break;
 981	case V4L2_CID_ALPHA_COMPONENT:
 982		ctx->d_frame.alpha = ctrl->val;
 983		break;
 984	}
 985
 986	return 0;
 987}
 988
 989static const struct v4l2_ctrl_ops mtk_mdp_ctrl_ops = {
 990	.s_ctrl = mtk_mdp_s_ctrl,
 991};
 992
 993static int mtk_mdp_ctrls_create(struct mtk_mdp_ctx *ctx)
 994{
 995	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MTK_MDP_MAX_CTRL_NUM);
 996
 997	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
 998			&mtk_mdp_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
 999	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
1000					     &mtk_mdp_ctrl_ops,
1001					     V4L2_CID_HFLIP,
1002					     0, 1, 1, 0);
1003	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
1004					     &mtk_mdp_ctrl_ops,
1005					     V4L2_CID_VFLIP,
1006					     0, 1, 1, 0);
1007	ctx->ctrls.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
1008						    &mtk_mdp_ctrl_ops,
1009						    V4L2_CID_ALPHA_COMPONENT,
1010						    0, 255, 1, 0);
1011	ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
1012
1013	if (ctx->ctrl_handler.error) {
1014		int err = ctx->ctrl_handler.error;
1015
1016		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
1017		dev_err(&ctx->mdp_dev->pdev->dev,
1018			"Failed to create control handlers\n");
1019		return err;
1020	}
1021
1022	return 0;
1023}
1024
1025static void mtk_mdp_set_default_params(struct mtk_mdp_ctx *ctx)
1026{
1027	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
1028	struct mtk_mdp_frame *frame;
1029
1030	frame = mtk_mdp_ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
1031	frame->fmt = mtk_mdp_find_fmt_by_index(0,
1032					V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
1033	frame->width = mdp->variant->pix_min->org_w;
1034	frame->height = mdp->variant->pix_min->org_h;
1035	frame->payload[0] = frame->width * frame->height;
1036	frame->payload[1] = frame->payload[0] / 2;
1037
1038	frame = mtk_mdp_ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
1039	frame->fmt = mtk_mdp_find_fmt_by_index(0,
1040					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
1041	frame->width = mdp->variant->pix_min->target_rot_dis_w;
1042	frame->height = mdp->variant->pix_min->target_rot_dis_h;
1043	frame->payload[0] = frame->width * frame->height;
1044	frame->payload[1] = frame->payload[0] / 2;
1045
1046}
1047
1048static int mtk_mdp_m2m_open(struct file *file)
1049{
1050	struct mtk_mdp_dev *mdp = video_drvdata(file);
1051	struct video_device *vfd = video_devdata(file);
1052	struct mtk_mdp_ctx *ctx = NULL;
1053	int ret;
1054	struct v4l2_format default_format;
1055
1056	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
1057	if (!ctx)
1058		return -ENOMEM;
1059
1060	if (mutex_lock_interruptible(&mdp->lock)) {
1061		ret = -ERESTARTSYS;
1062		goto err_lock;
1063	}
1064
1065	mutex_init(&ctx->slock);
1066	ctx->id = mdp->id_counter++;
1067	v4l2_fh_init(&ctx->fh, vfd);
1068	ret = mtk_mdp_ctrls_create(ctx);
1069	if (ret)
1070		goto error_ctrls;
1071
1072	/* Use separate control handler per file handle */
1073	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
1074	v4l2_fh_add(&ctx->fh, file);
1075	INIT_LIST_HEAD(&ctx->list);
1076
1077	ctx->mdp_dev = mdp;
1078	mtk_mdp_set_default_params(ctx);
1079
1080	INIT_WORK(&ctx->work, mtk_mdp_m2m_worker);
1081	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx,
1082					 mtk_mdp_m2m_queue_init);
1083	if (IS_ERR(ctx->m2m_ctx)) {
1084		dev_err(&mdp->pdev->dev, "Failed to initialize m2m context");
1085		ret = PTR_ERR(ctx->m2m_ctx);
1086		goto error_m2m_ctx;
1087	}
1088	ctx->fh.m2m_ctx = ctx->m2m_ctx;
1089	if (mdp->ctx_num++ == 0) {
1090		ret = vpu_load_firmware(mdp->vpu_dev);
1091		if (ret < 0) {
1092			dev_err(&mdp->pdev->dev,
1093				"vpu_load_firmware failed %d\n", ret);
1094			goto err_load_vpu;
1095		}
1096
1097		ret = mtk_mdp_vpu_register(mdp->pdev);
1098		if (ret < 0) {
1099			dev_err(&mdp->pdev->dev,
1100				"mdp_vpu register failed %d\n", ret);
1101			goto err_load_vpu;
1102		}
1103	}
1104
1105	list_add(&ctx->list, &mdp->ctx_list);
1106	mutex_unlock(&mdp->lock);
1107
1108	/* Default format */
1109	memset(&default_format, 0, sizeof(default_format));
1110	default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1111	default_format.fmt.pix_mp.width = 32;
1112	default_format.fmt.pix_mp.height = 32;
1113	default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
1114	mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
1115	default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1116	mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
1117
1118	mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
1119
1120	return 0;
1121
1122err_load_vpu:
1123	mdp->ctx_num--;
1124	v4l2_m2m_ctx_release(ctx->m2m_ctx);
1125error_m2m_ctx:
1126	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
1127error_ctrls:
1128	v4l2_fh_del(&ctx->fh, file);
1129	v4l2_fh_exit(&ctx->fh);
1130	mutex_unlock(&mdp->lock);
1131err_lock:
1132	kfree(ctx);
1133
1134	return ret;
1135}
1136
1137static int mtk_mdp_m2m_release(struct file *file)
1138{
1139	struct mtk_mdp_ctx *ctx = file_to_ctx(file);
1140	struct mtk_mdp_dev *mdp = ctx->mdp_dev;
1141
1142	flush_workqueue(mdp->job_wq);
1143	mutex_lock(&mdp->lock);
1144	v4l2_m2m_ctx_release(ctx->m2m_ctx);
1145	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
1146	v4l2_fh_del(&ctx->fh, file);
1147	v4l2_fh_exit(&ctx->fh);
1148	mtk_mdp_vpu_deinit(&ctx->vpu);
1149	mdp->ctx_num--;
1150	list_del_init(&ctx->list);
1151
1152	mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id);
1153
1154	mutex_unlock(&mdp->lock);
1155	kfree(ctx);
1156
1157	return 0;
1158}
1159
1160static const struct v4l2_file_operations mtk_mdp_m2m_fops = {
1161	.owner		= THIS_MODULE,
1162	.open		= mtk_mdp_m2m_open,
1163	.release	= mtk_mdp_m2m_release,
1164	.poll		= v4l2_m2m_fop_poll,
1165	.unlocked_ioctl	= video_ioctl2,
1166	.mmap		= v4l2_m2m_fop_mmap,
1167};
1168
1169static const struct v4l2_m2m_ops mtk_mdp_m2m_ops = {
1170	.device_run	= mtk_mdp_m2m_device_run,
1171};
1172
1173int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp)
1174{
1175	struct device *dev = &mdp->pdev->dev;
1176	int ret;
1177
1178	mdp->variant = &mtk_mdp_default_variant;
1179	mdp->vdev = video_device_alloc();
1180	if (!mdp->vdev) {
1181		dev_err(dev, "failed to allocate video device\n");
1182		ret = -ENOMEM;
1183		goto err_video_alloc;
1184	}
1185	mdp->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
1186	mdp->vdev->fops = &mtk_mdp_m2m_fops;
1187	mdp->vdev->ioctl_ops = &mtk_mdp_m2m_ioctl_ops;
1188	mdp->vdev->release = video_device_release;
1189	mdp->vdev->lock = &mdp->lock;
1190	mdp->vdev->vfl_dir = VFL_DIR_M2M;
1191	mdp->vdev->v4l2_dev = &mdp->v4l2_dev;
1192	snprintf(mdp->vdev->name, sizeof(mdp->vdev->name), "%s:m2m",
1193		 MTK_MDP_MODULE_NAME);
1194	video_set_drvdata(mdp->vdev, mdp);
1195
1196	mdp->m2m_dev = v4l2_m2m_init(&mtk_mdp_m2m_ops);
1197	if (IS_ERR(mdp->m2m_dev)) {
1198		dev_err(dev, "failed to initialize v4l2-m2m device\n");
1199		ret = PTR_ERR(mdp->m2m_dev);
1200		goto err_m2m_init;
1201	}
1202
1203	ret = video_register_device(mdp->vdev, VFL_TYPE_VIDEO, 2);
1204	if (ret) {
1205		dev_err(dev, "failed to register video device\n");
1206		goto err_vdev_register;
1207	}
1208
1209	v4l2_info(&mdp->v4l2_dev, "driver registered as /dev/video%d",
1210		  mdp->vdev->num);
1211	return 0;
1212
1213err_vdev_register:
1214	v4l2_m2m_release(mdp->m2m_dev);
1215err_m2m_init:
1216	video_device_release(mdp->vdev);
1217err_video_alloc:
1218
1219	return ret;
1220}
1221
1222void mtk_mdp_unregister_m2m_device(struct mtk_mdp_dev *mdp)
1223{
1224	video_unregister_device(mdp->vdev);
1225	v4l2_m2m_release(mdp->m2m_dev);
1226}
~/ Read-only file preview.