162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Allwinner sun8i deinterlacer with scaler driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on vim2m driver.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/iopoll.h>
1462306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/reset.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <media/v4l2-device.h>
2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2262306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "sun8i-di.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define FLAG_SIZE (DEINTERLACE_MAX_WIDTH * DEINTERLACE_MAX_HEIGHT / 4)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic u32 deinterlace_formats[] = {
2962306a36Sopenharmony_ci	V4L2_PIX_FMT_NV12,
3062306a36Sopenharmony_ci	V4L2_PIX_FMT_NV21,
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return readl(dev->base + reg);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void deinterlace_write(struct deinterlace_dev *dev,
3962306a36Sopenharmony_ci				     u32 reg, u32 value)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	writel(value, dev->base + reg);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic inline void deinterlace_set_bits(struct deinterlace_dev *dev,
4562306a36Sopenharmony_ci					u32 reg, u32 bits)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	writel(readl(dev->base + reg) | bits, dev->base + reg);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline void deinterlace_clr_set_bits(struct deinterlace_dev *dev,
5162306a36Sopenharmony_ci					    u32 reg, u32 clr, u32 set)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 val = readl(dev->base + reg);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	val &= ~clr;
5662306a36Sopenharmony_ci	val |= set;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	writel(val, dev->base + reg);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void deinterlace_device_run(void *priv)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
6462306a36Sopenharmony_ci	struct deinterlace_dev *dev = ctx->dev;
6562306a36Sopenharmony_ci	u32 size, stride, width, height, val;
6662306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
6762306a36Sopenharmony_ci	unsigned int hstep, vstep;
6862306a36Sopenharmony_ci	dma_addr_t addr;
6962306a36Sopenharmony_ci	int i;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
7262306a36Sopenharmony_ci	dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	v4l2_m2m_buf_copy_metadata(src, dst, true);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_MOD_ENABLE,
7762306a36Sopenharmony_ci			  DEINTERLACE_MOD_ENABLE_EN);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (ctx->field) {
8062306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
8162306a36Sopenharmony_ci				  ctx->flag1_buf_dma);
8262306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
8362306a36Sopenharmony_ci				  ctx->flag2_buf_dma);
8462306a36Sopenharmony_ci	} else {
8562306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
8662306a36Sopenharmony_ci				  ctx->flag2_buf_dma);
8762306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
8862306a36Sopenharmony_ci				  ctx->flag1_buf_dma);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_FLAG_LINE_STRIDE, 0x200);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	width = ctx->src_fmt.width;
9362306a36Sopenharmony_ci	height = ctx->src_fmt.height;
9462306a36Sopenharmony_ci	stride = ctx->src_fmt.bytesperline;
9562306a36Sopenharmony_ci	size = stride * height;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0);
9862306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR0, addr);
9962306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR1, addr + size);
10062306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR2, 0);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_LINE_STRIDE0, stride);
10362306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_LINE_STRIDE1, stride);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_IN_SIZE,
10662306a36Sopenharmony_ci			  DEINTERLACE_SIZE(width, height));
10762306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_IN_SIZE,
10862306a36Sopenharmony_ci			  DEINTERLACE_SIZE(width / 2, height / 2));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	val = DEINTERLACE_IN_FMT_FMT(DEINTERLACE_IN_FMT_YUV420) |
11162306a36Sopenharmony_ci	      DEINTERLACE_IN_FMT_MOD(DEINTERLACE_MODE_UV_COMBINED);
11262306a36Sopenharmony_ci	switch (ctx->src_fmt.pixelformat) {
11362306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
11462306a36Sopenharmony_ci		val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_UVUV);
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
11762306a36Sopenharmony_ci		val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_VUVU);
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_IN_FMT, val);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (ctx->prev)
12362306a36Sopenharmony_ci		addr = vb2_dma_contig_plane_dma_addr(&ctx->prev->vb2_buf, 0);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_PRELUMA, addr);
12662306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_PRECHROMA, addr + size);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	val = DEINTERLACE_OUT_FMT_FMT(DEINTERLACE_OUT_FMT_YUV420SP);
12962306a36Sopenharmony_ci	switch (ctx->src_fmt.pixelformat) {
13062306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
13162306a36Sopenharmony_ci		val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_UVUV);
13262306a36Sopenharmony_ci		break;
13362306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
13462306a36Sopenharmony_ci		val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_VUVU);
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_OUT_FMT, val);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	width = ctx->dst_fmt.width;
14062306a36Sopenharmony_ci	height = ctx->dst_fmt.height;
14162306a36Sopenharmony_ci	stride = ctx->dst_fmt.bytesperline;
14262306a36Sopenharmony_ci	size = stride * height;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_OUT_SIZE,
14562306a36Sopenharmony_ci			  DEINTERLACE_SIZE(width, height));
14662306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_OUT_SIZE,
14762306a36Sopenharmony_ci			  DEINTERLACE_SIZE(width / 2, height / 2));
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE0, stride);
15062306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE1, stride);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0);
15362306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR0, addr);
15462306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR1, addr + size);
15562306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR2, 0);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	hstep = (ctx->src_fmt.width << 16) / ctx->dst_fmt.width;
15862306a36Sopenharmony_ci	vstep = (ctx->src_fmt.height << 16) / ctx->dst_fmt.height;
15962306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_HORZ_FACT, hstep);
16062306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_VERT_FACT, vstep);
16162306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_HORZ_FACT, hstep);
16262306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_VERT_FACT, vstep);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* neutral filter coefficients */
16562306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
16662306a36Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_COEF_ACCESS);
16762306a36Sopenharmony_ci	readl_poll_timeout(dev->base + DEINTERLACE_STATUS, val,
16862306a36Sopenharmony_ci			   val & DEINTERLACE_STATUS_COEF_STATUS, 2, 40);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
17162306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH0_HORZ_COEF0 + i * 4,
17262306a36Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
17362306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH0_VERT_COEF + i * 4,
17462306a36Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
17562306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH1_HORZ_COEF0 + i * 4,
17662306a36Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
17762306a36Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH1_VERT_COEF + i * 4,
17862306a36Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
18262306a36Sopenharmony_ci				 DEINTERLACE_FRM_CTRL_COEF_ACCESS, 0);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FIELD_CTRL,
18562306a36Sopenharmony_ci				 DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK,
18662306a36Sopenharmony_ci				 DEINTERLACE_FIELD_CTRL_FIELD_CNT(ctx->field));
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
18962306a36Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_START);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
19262306a36Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_REG_READY);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_INT_ENABLE,
19562306a36Sopenharmony_ci			     DEINTERLACE_INT_ENABLE_WB_EN);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
19862306a36Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_WB_EN);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int deinterlace_job_ready(void *priv)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 &&
20662306a36Sopenharmony_ci	       v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void deinterlace_job_abort(void *priv)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Will cancel the transaction in the next interrupt handler */
21462306a36Sopenharmony_ci	ctx->aborting = 1;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic irqreturn_t deinterlace_irq(int irq, void *data)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct deinterlace_dev *dev = data;
22062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
22162306a36Sopenharmony_ci	enum vb2_buffer_state state;
22262306a36Sopenharmony_ci	struct deinterlace_ctx *ctx;
22362306a36Sopenharmony_ci	unsigned int val;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
22662306a36Sopenharmony_ci	if (!ctx) {
22762306a36Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
22862306a36Sopenharmony_ci			 "Instance released before the end of transaction\n");
22962306a36Sopenharmony_ci		return IRQ_NONE;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	val = deinterlace_read(dev, DEINTERLACE_INT_STATUS);
23362306a36Sopenharmony_ci	if (!(val & DEINTERLACE_INT_STATUS_WRITEBACK))
23462306a36Sopenharmony_ci		return IRQ_NONE;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_INT_ENABLE, 0);
23762306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_INT_STATUS,
23862306a36Sopenharmony_ci			     DEINTERLACE_INT_STATUS_WRITEBACK);
23962306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 0);
24062306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
24162306a36Sopenharmony_ci				 DEINTERLACE_FRM_CTRL_START, 0);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	val = deinterlace_read(dev, DEINTERLACE_STATUS);
24462306a36Sopenharmony_ci	if (val & DEINTERLACE_STATUS_WB_ERROR)
24562306a36Sopenharmony_ci		state = VB2_BUF_STATE_ERROR;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		state = VB2_BUF_STATE_DONE;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
25062306a36Sopenharmony_ci	v4l2_m2m_buf_done(dst, state);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (ctx->field != ctx->first_field || ctx->aborting) {
25362306a36Sopenharmony_ci		ctx->field = ctx->first_field;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
25662306a36Sopenharmony_ci		if (ctx->prev)
25762306a36Sopenharmony_ci			v4l2_m2m_buf_done(ctx->prev, state);
25862306a36Sopenharmony_ci		ctx->prev = src;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
26162306a36Sopenharmony_ci	} else {
26262306a36Sopenharmony_ci		ctx->field = !ctx->first_field;
26362306a36Sopenharmony_ci		deinterlace_device_run(ctx);
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return IRQ_HANDLED;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void deinterlace_init(struct deinterlace_dev *dev)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	u32 val;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BYPASS,
27462306a36Sopenharmony_ci			  DEINTERLACE_BYPASS_CSC);
27562306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE_CTRL,
27662306a36Sopenharmony_ci			  DEINTERLACE_WB_LINE_STRIDE_CTRL_EN);
27762306a36Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
27862306a36Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_OUT_CTRL);
27962306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_AGTH_SEL,
28062306a36Sopenharmony_ci			  DEINTERLACE_AGTH_SEL_LINEBUF);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	val = DEINTERLACE_CTRL_EN |
28362306a36Sopenharmony_ci	      DEINTERLACE_CTRL_MODE_MIXED |
28462306a36Sopenharmony_ci	      DEINTERLACE_CTRL_DIAG_INTP_EN |
28562306a36Sopenharmony_ci	      DEINTERLACE_CTRL_TEMP_DIFF_EN;
28662306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CTRL, val);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_LUMA_TH,
28962306a36Sopenharmony_ci				 DEINTERLACE_LUMA_TH_MIN_LUMA_MSK,
29062306a36Sopenharmony_ci				 DEINTERLACE_LUMA_TH_MIN_LUMA(4));
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_SPAT_COMP,
29362306a36Sopenharmony_ci				 DEINTERLACE_SPAT_COMP_TH2_MSK,
29462306a36Sopenharmony_ci				 DEINTERLACE_SPAT_COMP_TH2(5));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_TEMP_DIFF,
29762306a36Sopenharmony_ci				 DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK,
29862306a36Sopenharmony_ci				 DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(5));
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	val = DEINTERLACE_DIAG_INTP_TH0(60) |
30162306a36Sopenharmony_ci	      DEINTERLACE_DIAG_INTP_TH1(0) |
30262306a36Sopenharmony_ci	      DEINTERLACE_DIAG_INTP_TH3(30);
30362306a36Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_DIAG_INTP, val);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_CHROMA_DIFF,
30662306a36Sopenharmony_ci				 DEINTERLACE_CHROMA_DIFF_TH_MSK,
30762306a36Sopenharmony_ci				 DEINTERLACE_CHROMA_DIFF_TH(31));
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	return container_of(file->private_data, struct deinterlace_ctx, fh);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic bool deinterlace_check_format(u32 pixelformat)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	unsigned int i;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++)
32062306a36Sopenharmony_ci		if (deinterlace_formats[i] == pixelformat)
32162306a36Sopenharmony_ci			return true;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return false;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	unsigned int height = pix_fmt->height;
32962306a36Sopenharmony_ci	unsigned int width = pix_fmt->width;
33062306a36Sopenharmony_ci	unsigned int bytesperline;
33162306a36Sopenharmony_ci	unsigned int sizeimage;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	width = clamp(width, DEINTERLACE_MIN_WIDTH,
33462306a36Sopenharmony_ci		      DEINTERLACE_MAX_WIDTH);
33562306a36Sopenharmony_ci	height = clamp(height, DEINTERLACE_MIN_HEIGHT,
33662306a36Sopenharmony_ci		       DEINTERLACE_MAX_HEIGHT);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	bytesperline = ALIGN(width, 2);
33962306a36Sopenharmony_ci	/* luma */
34062306a36Sopenharmony_ci	sizeimage = bytesperline * height;
34162306a36Sopenharmony_ci	/* chroma */
34262306a36Sopenharmony_ci	sizeimage += bytesperline * height / 2;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	pix_fmt->width = width;
34562306a36Sopenharmony_ci	pix_fmt->height = height;
34662306a36Sopenharmony_ci	pix_fmt->bytesperline = bytesperline;
34762306a36Sopenharmony_ci	pix_fmt->sizeimage = sizeimage;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int deinterlace_querycap(struct file *file, void *priv,
35162306a36Sopenharmony_ci				struct v4l2_capability *cap)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver));
35462306a36Sopenharmony_ci	strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card));
35562306a36Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
35662306a36Sopenharmony_ci		 "platform:%s", DEINTERLACE_NAME);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int deinterlace_enum_fmt(struct file *file, void *priv,
36262306a36Sopenharmony_ci				struct v4l2_fmtdesc *f)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	if (f->index < ARRAY_SIZE(deinterlace_formats)) {
36562306a36Sopenharmony_ci		f->pixelformat = deinterlace_formats[f->index];
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		return 0;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return -EINVAL;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int deinterlace_enum_framesizes(struct file *file, void *priv,
37462306a36Sopenharmony_ci				       struct v4l2_frmsizeenum *fsize)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	if (fsize->index != 0)
37762306a36Sopenharmony_ci		return -EINVAL;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!deinterlace_check_format(fsize->pixel_format))
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
38362306a36Sopenharmony_ci	fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH;
38462306a36Sopenharmony_ci	fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT;
38562306a36Sopenharmony_ci	fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH;
38662306a36Sopenharmony_ci	fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT;
38762306a36Sopenharmony_ci	fsize->stepwise.step_width = 2;
38862306a36Sopenharmony_ci	fsize->stepwise.step_height = 1;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int deinterlace_g_fmt_vid_cap(struct file *file, void *priv,
39462306a36Sopenharmony_ci				     struct v4l2_format *f)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	f->fmt.pix = ctx->dst_fmt;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int deinterlace_g_fmt_vid_out(struct file *file, void *priv,
40462306a36Sopenharmony_ci				     struct v4l2_format *f)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	f->fmt.pix = ctx->src_fmt;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int deinterlace_try_fmt_vid_cap(struct file *file, void *priv,
41462306a36Sopenharmony_ci				       struct v4l2_format *f)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	if (!deinterlace_check_format(f->fmt.pix.pixelformat))
41762306a36Sopenharmony_ci		f->fmt.pix.pixelformat = deinterlace_formats[0];
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (f->fmt.pix.field != V4L2_FIELD_NONE)
42062306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_NONE;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	deinterlace_prepare_format(&f->fmt.pix);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int deinterlace_try_fmt_vid_out(struct file *file, void *priv,
42862306a36Sopenharmony_ci				       struct v4l2_format *f)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	if (!deinterlace_check_format(f->fmt.pix.pixelformat))
43162306a36Sopenharmony_ci		f->fmt.pix.pixelformat = deinterlace_formats[0];
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
43462306a36Sopenharmony_ci	    f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
43562306a36Sopenharmony_ci	    f->fmt.pix.field != V4L2_FIELD_INTERLACED)
43662306a36Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	deinterlace_prepare_format(&f->fmt.pix);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic int deinterlace_s_fmt_vid_cap(struct file *file, void *priv,
44462306a36Sopenharmony_ci				     struct v4l2_format *f)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
44762306a36Sopenharmony_ci	struct vb2_queue *vq;
44862306a36Sopenharmony_ci	int ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ret = deinterlace_try_fmt_vid_cap(file, priv, f);
45162306a36Sopenharmony_ci	if (ret)
45262306a36Sopenharmony_ci		return ret;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
45562306a36Sopenharmony_ci	if (vb2_is_busy(vq))
45662306a36Sopenharmony_ci		return -EBUSY;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ctx->dst_fmt = f->fmt.pix;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return 0;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int deinterlace_s_fmt_vid_out(struct file *file, void *priv,
46462306a36Sopenharmony_ci				     struct v4l2_format *f)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
46762306a36Sopenharmony_ci	struct vb2_queue *vq;
46862306a36Sopenharmony_ci	int ret;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	ret = deinterlace_try_fmt_vid_out(file, priv, f);
47162306a36Sopenharmony_ci	if (ret)
47262306a36Sopenharmony_ci		return ret;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
47562306a36Sopenharmony_ci	if (vb2_is_busy(vq))
47662306a36Sopenharmony_ci		return -EBUSY;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	ctx->src_fmt = f->fmt.pix;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* Propagate colorspace information to capture. */
48162306a36Sopenharmony_ci	ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
48262306a36Sopenharmony_ci	ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
48362306a36Sopenharmony_ci	ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
48462306a36Sopenharmony_ci	ctx->dst_fmt.quantization = f->fmt.pix.quantization;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
49062306a36Sopenharmony_ci	.vidioc_querycap		= deinterlace_querycap,
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	.vidioc_enum_framesizes		= deinterlace_enum_framesizes,
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= deinterlace_enum_fmt,
49562306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= deinterlace_g_fmt_vid_cap,
49662306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= deinterlace_try_fmt_vid_cap,
49762306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= deinterlace_s_fmt_vid_cap,
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= deinterlace_enum_fmt,
50062306a36Sopenharmony_ci	.vidioc_g_fmt_vid_out		= deinterlace_g_fmt_vid_out,
50162306a36Sopenharmony_ci	.vidioc_try_fmt_vid_out		= deinterlace_try_fmt_vid_out,
50262306a36Sopenharmony_ci	.vidioc_s_fmt_vid_out		= deinterlace_s_fmt_vid_out,
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
50562306a36Sopenharmony_ci	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
50662306a36Sopenharmony_ci	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
50762306a36Sopenharmony_ci	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
50862306a36Sopenharmony_ci	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
50962306a36Sopenharmony_ci	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
51062306a36Sopenharmony_ci	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
51362306a36Sopenharmony_ci	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
51762306a36Sopenharmony_ci				   unsigned int *nplanes, unsigned int sizes[],
51862306a36Sopenharmony_ci				   struct device *alloc_devs[])
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
52162306a36Sopenharmony_ci	struct v4l2_pix_format *pix_fmt;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type))
52462306a36Sopenharmony_ci		pix_fmt = &ctx->src_fmt;
52562306a36Sopenharmony_ci	else
52662306a36Sopenharmony_ci		pix_fmt = &ctx->dst_fmt;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (*nplanes) {
52962306a36Sopenharmony_ci		if (sizes[0] < pix_fmt->sizeimage)
53062306a36Sopenharmony_ci			return -EINVAL;
53162306a36Sopenharmony_ci	} else {
53262306a36Sopenharmony_ci		sizes[0] = pix_fmt->sizeimage;
53362306a36Sopenharmony_ci		*nplanes = 1;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int deinterlace_buf_prepare(struct vb2_buffer *vb)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
54262306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
54362306a36Sopenharmony_ci	struct v4l2_pix_format *pix_fmt;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type))
54662306a36Sopenharmony_ci		pix_fmt = &ctx->src_fmt;
54762306a36Sopenharmony_ci	else
54862306a36Sopenharmony_ci		pix_fmt = &ctx->dst_fmt;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
55162306a36Sopenharmony_ci		return -EINVAL;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic void deinterlace_buf_queue(struct vb2_buffer *vb)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
56162306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
56962306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	do {
57262306a36Sopenharmony_ci		if (V4L2_TYPE_IS_OUTPUT(vq->type))
57362306a36Sopenharmony_ci			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
57462306a36Sopenharmony_ci		else
57562306a36Sopenharmony_ci			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		if (vbuf)
57862306a36Sopenharmony_ci			v4l2_m2m_buf_done(vbuf, state);
57962306a36Sopenharmony_ci	} while (vbuf);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev)
58262306a36Sopenharmony_ci		v4l2_m2m_buf_done(ctx->prev, state);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
58862306a36Sopenharmony_ci	struct device *dev = ctx->dev->dev;
58962306a36Sopenharmony_ci	int ret;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
59262306a36Sopenharmony_ci		ret = pm_runtime_resume_and_get(dev);
59362306a36Sopenharmony_ci		if (ret < 0) {
59462306a36Sopenharmony_ci			dev_err(dev, "Failed to enable module\n");
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci			goto err_runtime_get;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		ctx->first_field =
60062306a36Sopenharmony_ci			ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT;
60162306a36Sopenharmony_ci		ctx->field = ctx->first_field;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		ctx->prev = NULL;
60462306a36Sopenharmony_ci		ctx->aborting = 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE,
60762306a36Sopenharmony_ci						    &ctx->flag1_buf_dma,
60862306a36Sopenharmony_ci						    GFP_KERNEL);
60962306a36Sopenharmony_ci		if (!ctx->flag1_buf) {
61062306a36Sopenharmony_ci			ret = -ENOMEM;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci			goto err_no_mem1;
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE,
61662306a36Sopenharmony_ci						    &ctx->flag2_buf_dma,
61762306a36Sopenharmony_ci						    GFP_KERNEL);
61862306a36Sopenharmony_ci		if (!ctx->flag2_buf) {
61962306a36Sopenharmony_ci			ret = -ENOMEM;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci			goto err_no_mem2;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return 0;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cierr_no_mem2:
62862306a36Sopenharmony_ci	dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
62962306a36Sopenharmony_ci			  ctx->flag1_buf_dma);
63062306a36Sopenharmony_cierr_no_mem1:
63162306a36Sopenharmony_ci	pm_runtime_put(dev);
63262306a36Sopenharmony_cierr_runtime_get:
63362306a36Sopenharmony_ci	deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return ret;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic void deinterlace_stop_streaming(struct vb2_queue *vq)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
64362306a36Sopenharmony_ci		struct device *dev = ctx->dev->dev;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
64662306a36Sopenharmony_ci				  ctx->flag1_buf_dma);
64762306a36Sopenharmony_ci		dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf,
64862306a36Sopenharmony_ci				  ctx->flag2_buf_dma);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		pm_runtime_put(dev);
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic const struct vb2_ops deinterlace_qops = {
65762306a36Sopenharmony_ci	.queue_setup		= deinterlace_queue_setup,
65862306a36Sopenharmony_ci	.buf_prepare		= deinterlace_buf_prepare,
65962306a36Sopenharmony_ci	.buf_queue		= deinterlace_buf_queue,
66062306a36Sopenharmony_ci	.start_streaming	= deinterlace_start_streaming,
66162306a36Sopenharmony_ci	.stop_streaming		= deinterlace_stop_streaming,
66262306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
66362306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
66462306a36Sopenharmony_ci};
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq,
66762306a36Sopenharmony_ci				  struct vb2_queue *dst_vq)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
67062306a36Sopenharmony_ci	int ret;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
67362306a36Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
67462306a36Sopenharmony_ci	src_vq->drv_priv = ctx;
67562306a36Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
67662306a36Sopenharmony_ci	src_vq->min_buffers_needed = 1;
67762306a36Sopenharmony_ci	src_vq->ops = &deinterlace_qops;
67862306a36Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
67962306a36Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
68062306a36Sopenharmony_ci	src_vq->lock = &ctx->dev->dev_mutex;
68162306a36Sopenharmony_ci	src_vq->dev = ctx->dev->dev;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	ret = vb2_queue_init(src_vq);
68462306a36Sopenharmony_ci	if (ret)
68562306a36Sopenharmony_ci		return ret;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
68862306a36Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
68962306a36Sopenharmony_ci	dst_vq->drv_priv = ctx;
69062306a36Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
69162306a36Sopenharmony_ci	dst_vq->min_buffers_needed = 2;
69262306a36Sopenharmony_ci	dst_vq->ops = &deinterlace_qops;
69362306a36Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
69462306a36Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
69562306a36Sopenharmony_ci	dst_vq->lock = &ctx->dev->dev_mutex;
69662306a36Sopenharmony_ci	dst_vq->dev = ctx->dev->dev;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = vb2_queue_init(dst_vq);
69962306a36Sopenharmony_ci	if (ret)
70062306a36Sopenharmony_ci		return ret;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int deinterlace_open(struct file *file)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct deinterlace_dev *dev = video_drvdata(file);
70862306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = NULL;
70962306a36Sopenharmony_ci	int ret;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->dev_mutex))
71262306a36Sopenharmony_ci		return -ERESTARTSYS;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
71562306a36Sopenharmony_ci	if (!ctx) {
71662306a36Sopenharmony_ci		mutex_unlock(&dev->dev_mutex);
71762306a36Sopenharmony_ci		return -ENOMEM;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* default output format */
72162306a36Sopenharmony_ci	ctx->src_fmt.pixelformat = deinterlace_formats[0];
72262306a36Sopenharmony_ci	ctx->src_fmt.field = V4L2_FIELD_INTERLACED;
72362306a36Sopenharmony_ci	ctx->src_fmt.width = 640;
72462306a36Sopenharmony_ci	ctx->src_fmt.height = 480;
72562306a36Sopenharmony_ci	deinterlace_prepare_format(&ctx->src_fmt);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* default capture format */
72862306a36Sopenharmony_ci	ctx->dst_fmt.pixelformat = deinterlace_formats[0];
72962306a36Sopenharmony_ci	ctx->dst_fmt.field = V4L2_FIELD_NONE;
73062306a36Sopenharmony_ci	ctx->dst_fmt.width = 640;
73162306a36Sopenharmony_ci	ctx->dst_fmt.height = 480;
73262306a36Sopenharmony_ci	deinterlace_prepare_format(&ctx->dst_fmt);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	v4l2_fh_init(&ctx->fh, video_devdata(file));
73562306a36Sopenharmony_ci	file->private_data = &ctx->fh;
73662306a36Sopenharmony_ci	ctx->dev = dev;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
73962306a36Sopenharmony_ci					    &deinterlace_queue_init);
74062306a36Sopenharmony_ci	if (IS_ERR(ctx->fh.m2m_ctx)) {
74162306a36Sopenharmony_ci		ret = PTR_ERR(ctx->fh.m2m_ctx);
74262306a36Sopenharmony_ci		goto err_free;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	return 0;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cierr_free:
75262306a36Sopenharmony_ci	kfree(ctx);
75362306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return ret;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic int deinterlace_release(struct file *file)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct deinterlace_dev *dev = video_drvdata(file);
76162306a36Sopenharmony_ci	struct deinterlace_ctx *ctx = container_of(file->private_data,
76262306a36Sopenharmony_ci						   struct deinterlace_ctx, fh);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
76762306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
76862306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	kfree(ctx);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return 0;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic const struct v4l2_file_operations deinterlace_fops = {
77862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
77962306a36Sopenharmony_ci	.open		= deinterlace_open,
78062306a36Sopenharmony_ci	.release	= deinterlace_release,
78162306a36Sopenharmony_ci	.poll		= v4l2_m2m_fop_poll,
78262306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
78362306a36Sopenharmony_ci	.mmap		= v4l2_m2m_fop_mmap,
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic const struct video_device deinterlace_video_device = {
78762306a36Sopenharmony_ci	.name		= DEINTERLACE_NAME,
78862306a36Sopenharmony_ci	.vfl_dir	= VFL_DIR_M2M,
78962306a36Sopenharmony_ci	.fops		= &deinterlace_fops,
79062306a36Sopenharmony_ci	.ioctl_ops	= &deinterlace_ioctl_ops,
79162306a36Sopenharmony_ci	.minor		= -1,
79262306a36Sopenharmony_ci	.release	= video_device_release_empty,
79362306a36Sopenharmony_ci	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
79462306a36Sopenharmony_ci};
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic const struct v4l2_m2m_ops deinterlace_m2m_ops = {
79762306a36Sopenharmony_ci	.device_run	= deinterlace_device_run,
79862306a36Sopenharmony_ci	.job_ready	= deinterlace_job_ready,
79962306a36Sopenharmony_ci	.job_abort	= deinterlace_job_abort,
80062306a36Sopenharmony_ci};
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int deinterlace_probe(struct platform_device *pdev)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct deinterlace_dev *dev;
80562306a36Sopenharmony_ci	struct video_device *vfd;
80662306a36Sopenharmony_ci	int irq, ret;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
80962306a36Sopenharmony_ci	if (!dev)
81062306a36Sopenharmony_ci		return -ENOMEM;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	dev->vfd = deinterlace_video_device;
81362306a36Sopenharmony_ci	dev->dev = &pdev->dev;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
81662306a36Sopenharmony_ci	if (irq <= 0)
81762306a36Sopenharmony_ci		return irq;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ret = devm_request_irq(dev->dev, irq, deinterlace_irq,
82062306a36Sopenharmony_ci			       0, dev_name(dev->dev), dev);
82162306a36Sopenharmony_ci	if (ret) {
82262306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to request IRQ\n");
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		return ret;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	dev->base = devm_platform_ioremap_resource(pdev, 0);
82862306a36Sopenharmony_ci	if (IS_ERR(dev->base))
82962306a36Sopenharmony_ci		return PTR_ERR(dev->base);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	dev->bus_clk = devm_clk_get(dev->dev, "bus");
83262306a36Sopenharmony_ci	if (IS_ERR(dev->bus_clk)) {
83362306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to get bus clock\n");
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		return PTR_ERR(dev->bus_clk);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	dev->mod_clk = devm_clk_get(dev->dev, "mod");
83962306a36Sopenharmony_ci	if (IS_ERR(dev->mod_clk)) {
84062306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to get mod clock\n");
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		return PTR_ERR(dev->mod_clk);
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	dev->ram_clk = devm_clk_get(dev->dev, "ram");
84662306a36Sopenharmony_ci	if (IS_ERR(dev->ram_clk)) {
84762306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to get ram clock\n");
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		return PTR_ERR(dev->ram_clk);
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dev->rstc = devm_reset_control_get(dev->dev, NULL);
85362306a36Sopenharmony_ci	if (IS_ERR(dev->rstc)) {
85462306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to get reset control\n");
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		return PTR_ERR(dev->rstc);
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	mutex_init(&dev->dev_mutex);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
86262306a36Sopenharmony_ci	if (ret) {
86362306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to register V4L2 device\n");
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		return ret;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	vfd = &dev->vfd;
86962306a36Sopenharmony_ci	vfd->lock = &dev->dev_mutex;
87062306a36Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s",
87362306a36Sopenharmony_ci		 deinterlace_video_device.name);
87462306a36Sopenharmony_ci	video_set_drvdata(vfd, dev);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
87762306a36Sopenharmony_ci	if (ret) {
87862306a36Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci		goto err_v4l2;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	v4l2_info(&dev->v4l2_dev,
88462306a36Sopenharmony_ci		  "Device registered as /dev/video%d\n", vfd->num);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops);
88762306a36Sopenharmony_ci	if (IS_ERR(dev->m2m_dev)) {
88862306a36Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
88962306a36Sopenharmony_ci			 "Failed to initialize V4L2 M2M device\n");
89062306a36Sopenharmony_ci		ret = PTR_ERR(dev->m2m_dev);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci		goto err_video;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	pm_runtime_enable(dev->dev);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return 0;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cierr_video:
90262306a36Sopenharmony_ci	video_unregister_device(&dev->vfd);
90362306a36Sopenharmony_cierr_v4l2:
90462306a36Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return ret;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic void deinterlace_remove(struct platform_device *pdev)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct deinterlace_dev *dev = platform_get_drvdata(pdev);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	v4l2_m2m_release(dev->m2m_dev);
91462306a36Sopenharmony_ci	video_unregister_device(&dev->vfd);
91562306a36Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	pm_runtime_force_suspend(&pdev->dev);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int deinterlace_runtime_resume(struct device *device)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct deinterlace_dev *dev = dev_get_drvdata(device);
92362306a36Sopenharmony_ci	int ret;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	ret = clk_set_rate_exclusive(dev->mod_clk, 300000000);
92662306a36Sopenharmony_ci	if (ret) {
92762306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to set exclusive mod clock rate\n");
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		return ret;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	ret = reset_control_deassert(dev->rstc);
93362306a36Sopenharmony_ci	if (ret) {
93462306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to apply reset\n");
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci		goto err_exclusive_rate;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	ret = clk_prepare_enable(dev->bus_clk);
94062306a36Sopenharmony_ci	if (ret) {
94162306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to enable bus clock\n");
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		goto err_rst;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	ret = clk_prepare_enable(dev->mod_clk);
94762306a36Sopenharmony_ci	if (ret) {
94862306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to enable mod clock\n");
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		goto err_bus_clk;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	ret = clk_prepare_enable(dev->ram_clk);
95462306a36Sopenharmony_ci	if (ret) {
95562306a36Sopenharmony_ci		dev_err(dev->dev, "Failed to enable ram clock\n");
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		goto err_mod_clk;
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	deinterlace_init(dev);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	return 0;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cierr_mod_clk:
96562306a36Sopenharmony_ci	clk_disable_unprepare(dev->mod_clk);
96662306a36Sopenharmony_cierr_bus_clk:
96762306a36Sopenharmony_ci	clk_disable_unprepare(dev->bus_clk);
96862306a36Sopenharmony_cierr_rst:
96962306a36Sopenharmony_ci	reset_control_assert(dev->rstc);
97062306a36Sopenharmony_cierr_exclusive_rate:
97162306a36Sopenharmony_ci	clk_rate_exclusive_put(dev->mod_clk);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	return ret;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic int deinterlace_runtime_suspend(struct device *device)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct deinterlace_dev *dev = dev_get_drvdata(device);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	clk_disable_unprepare(dev->ram_clk);
98162306a36Sopenharmony_ci	clk_disable_unprepare(dev->mod_clk);
98262306a36Sopenharmony_ci	clk_disable_unprepare(dev->bus_clk);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	reset_control_assert(dev->rstc);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	clk_rate_exclusive_put(dev->mod_clk);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return 0;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic const struct of_device_id deinterlace_dt_match[] = {
99262306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-h3-deinterlace" },
99362306a36Sopenharmony_ci	{ /* sentinel */ }
99462306a36Sopenharmony_ci};
99562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, deinterlace_dt_match);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic const struct dev_pm_ops deinterlace_pm_ops = {
99862306a36Sopenharmony_ci	.runtime_resume		= deinterlace_runtime_resume,
99962306a36Sopenharmony_ci	.runtime_suspend	= deinterlace_runtime_suspend,
100062306a36Sopenharmony_ci};
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic struct platform_driver deinterlace_driver = {
100362306a36Sopenharmony_ci	.probe		= deinterlace_probe,
100462306a36Sopenharmony_ci	.remove_new	= deinterlace_remove,
100562306a36Sopenharmony_ci	.driver		= {
100662306a36Sopenharmony_ci		.name		= DEINTERLACE_NAME,
100762306a36Sopenharmony_ci		.of_match_table	= deinterlace_dt_match,
100862306a36Sopenharmony_ci		.pm		= &deinterlace_pm_ops,
100962306a36Sopenharmony_ci	},
101062306a36Sopenharmony_ci};
101162306a36Sopenharmony_cimodule_platform_driver(deinterlace_driver);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
101462306a36Sopenharmony_ciMODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
101562306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner Deinterlace driver");
1016