18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Allwinner sun8i deinterlacer with scaler driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on vim2m driver.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/reset.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-mem2mem.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "sun8i-di.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define FLAG_SIZE (DEINTERLACE_MAX_WIDTH * DEINTERLACE_MAX_HEIGHT / 4)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic u32 deinterlace_formats[] = {
298c2ecf20Sopenharmony_ci	V4L2_PIX_FMT_NV12,
308c2ecf20Sopenharmony_ci	V4L2_PIX_FMT_NV21,
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return readl(dev->base + reg);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline void deinterlace_write(struct deinterlace_dev *dev,
398c2ecf20Sopenharmony_ci				     u32 reg, u32 value)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	writel(value, dev->base + reg);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic inline void deinterlace_set_bits(struct deinterlace_dev *dev,
458c2ecf20Sopenharmony_ci					u32 reg, u32 bits)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	writel(readl(dev->base + reg) | bits, dev->base + reg);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic inline void deinterlace_clr_set_bits(struct deinterlace_dev *dev,
518c2ecf20Sopenharmony_ci					    u32 reg, u32 clr, u32 set)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	u32 val = readl(dev->base + reg);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	val &= ~clr;
568c2ecf20Sopenharmony_ci	val |= set;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	writel(val, dev->base + reg);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void deinterlace_device_run(void *priv)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
648c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = ctx->dev;
658c2ecf20Sopenharmony_ci	u32 size, stride, width, height, val;
668c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
678c2ecf20Sopenharmony_ci	unsigned int hstep, vstep;
688c2ecf20Sopenharmony_ci	dma_addr_t addr;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
718c2ecf20Sopenharmony_ci	dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	v4l2_m2m_buf_copy_metadata(src, dst, true);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_MOD_ENABLE,
768c2ecf20Sopenharmony_ci			  DEINTERLACE_MOD_ENABLE_EN);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (ctx->field) {
798c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
808c2ecf20Sopenharmony_ci				  ctx->flag1_buf_dma);
818c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
828c2ecf20Sopenharmony_ci				  ctx->flag2_buf_dma);
838c2ecf20Sopenharmony_ci	} else {
848c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG0,
858c2ecf20Sopenharmony_ci				  ctx->flag2_buf_dma);
868c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_TILE_FLAG1,
878c2ecf20Sopenharmony_ci				  ctx->flag1_buf_dma);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_FLAG_LINE_STRIDE, 0x200);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	width = ctx->src_fmt.width;
928c2ecf20Sopenharmony_ci	height = ctx->src_fmt.height;
938c2ecf20Sopenharmony_ci	stride = ctx->src_fmt.bytesperline;
948c2ecf20Sopenharmony_ci	size = stride * height;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0);
978c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR0, addr);
988c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR1, addr + size);
998c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BUF_ADDR2, 0);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_LINE_STRIDE0, stride);
1028c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_LINE_STRIDE1, stride);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_IN_SIZE,
1058c2ecf20Sopenharmony_ci			  DEINTERLACE_SIZE(width, height));
1068c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_IN_SIZE,
1078c2ecf20Sopenharmony_ci			  DEINTERLACE_SIZE(width / 2, height / 2));
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	val = DEINTERLACE_IN_FMT_FMT(DEINTERLACE_IN_FMT_YUV420) |
1108c2ecf20Sopenharmony_ci	      DEINTERLACE_IN_FMT_MOD(DEINTERLACE_MODE_UV_COMBINED);
1118c2ecf20Sopenharmony_ci	switch (ctx->src_fmt.pixelformat) {
1128c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
1138c2ecf20Sopenharmony_ci		val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_UVUV);
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
1168c2ecf20Sopenharmony_ci		val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_VUVU);
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_IN_FMT, val);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (ctx->prev)
1228c2ecf20Sopenharmony_ci		addr = vb2_dma_contig_plane_dma_addr(&ctx->prev->vb2_buf, 0);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_PRELUMA, addr);
1258c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_PRECHROMA, addr + size);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	val = DEINTERLACE_OUT_FMT_FMT(DEINTERLACE_OUT_FMT_YUV420SP);
1288c2ecf20Sopenharmony_ci	switch (ctx->src_fmt.pixelformat) {
1298c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
1308c2ecf20Sopenharmony_ci		val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_UVUV);
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
1338c2ecf20Sopenharmony_ci		val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_VUVU);
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_OUT_FMT, val);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	width = ctx->dst_fmt.width;
1398c2ecf20Sopenharmony_ci	height = ctx->dst_fmt.height;
1408c2ecf20Sopenharmony_ci	stride = ctx->dst_fmt.bytesperline;
1418c2ecf20Sopenharmony_ci	size = stride * height;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_OUT_SIZE,
1448c2ecf20Sopenharmony_ci			  DEINTERLACE_SIZE(width, height));
1458c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_OUT_SIZE,
1468c2ecf20Sopenharmony_ci			  DEINTERLACE_SIZE(width / 2, height / 2));
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE0, stride);
1498c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE1, stride);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0);
1528c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR0, addr);
1538c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR1, addr + size);
1548c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_ADDR2, 0);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	hstep = (ctx->src_fmt.width << 16) / ctx->dst_fmt.width;
1578c2ecf20Sopenharmony_ci	vstep = (ctx->src_fmt.height << 16) / ctx->dst_fmt.height;
1588c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_HORZ_FACT, hstep);
1598c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH0_VERT_FACT, vstep);
1608c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_HORZ_FACT, hstep);
1618c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CH1_VERT_FACT, vstep);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FIELD_CTRL,
1648c2ecf20Sopenharmony_ci				 DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK,
1658c2ecf20Sopenharmony_ci				 DEINTERLACE_FIELD_CTRL_FIELD_CNT(ctx->field));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
1688c2ecf20Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_START);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
1718c2ecf20Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_REG_READY);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_INT_ENABLE,
1748c2ecf20Sopenharmony_ci			     DEINTERLACE_INT_ENABLE_WB_EN);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
1778c2ecf20Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_WB_EN);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int deinterlace_job_ready(void *priv)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 &&
1858c2ecf20Sopenharmony_ci	       v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void deinterlace_job_abort(void *priv)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Will cancel the transaction in the next interrupt handler */
1938c2ecf20Sopenharmony_ci	ctx->aborting = 1;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic irqreturn_t deinterlace_irq(int irq, void *data)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = data;
1998c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src, *dst;
2008c2ecf20Sopenharmony_ci	enum vb2_buffer_state state;
2018c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx;
2028c2ecf20Sopenharmony_ci	unsigned int val;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
2058c2ecf20Sopenharmony_ci	if (!ctx) {
2068c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
2078c2ecf20Sopenharmony_ci			 "Instance released before the end of transaction\n");
2088c2ecf20Sopenharmony_ci		return IRQ_NONE;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	val = deinterlace_read(dev, DEINTERLACE_INT_STATUS);
2128c2ecf20Sopenharmony_ci	if (!(val & DEINTERLACE_INT_STATUS_WRITEBACK))
2138c2ecf20Sopenharmony_ci		return IRQ_NONE;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_INT_ENABLE, 0);
2168c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_INT_STATUS,
2178c2ecf20Sopenharmony_ci			     DEINTERLACE_INT_STATUS_WRITEBACK);
2188c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 0);
2198c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
2208c2ecf20Sopenharmony_ci				 DEINTERLACE_FRM_CTRL_START, 0);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	val = deinterlace_read(dev, DEINTERLACE_STATUS);
2238c2ecf20Sopenharmony_ci	if (val & DEINTERLACE_STATUS_WB_ERROR)
2248c2ecf20Sopenharmony_ci		state = VB2_BUF_STATE_ERROR;
2258c2ecf20Sopenharmony_ci	else
2268c2ecf20Sopenharmony_ci		state = VB2_BUF_STATE_DONE;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
2298c2ecf20Sopenharmony_ci	v4l2_m2m_buf_done(dst, state);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (ctx->field != ctx->first_field || ctx->aborting) {
2328c2ecf20Sopenharmony_ci		ctx->field = ctx->first_field;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
2358c2ecf20Sopenharmony_ci		if (ctx->prev)
2368c2ecf20Sopenharmony_ci			v4l2_m2m_buf_done(ctx->prev, state);
2378c2ecf20Sopenharmony_ci		ctx->prev = src;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
2408c2ecf20Sopenharmony_ci	} else {
2418c2ecf20Sopenharmony_ci		ctx->field = !ctx->first_field;
2428c2ecf20Sopenharmony_ci		deinterlace_device_run(ctx);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void deinterlace_init(struct deinterlace_dev *dev)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	u32 val;
2518c2ecf20Sopenharmony_ci	int i;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_BYPASS,
2548c2ecf20Sopenharmony_ci			  DEINTERLACE_BYPASS_CSC);
2558c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE_CTRL,
2568c2ecf20Sopenharmony_ci			  DEINTERLACE_WB_LINE_STRIDE_CTRL_EN);
2578c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
2588c2ecf20Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_OUT_CTRL);
2598c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_AGTH_SEL,
2608c2ecf20Sopenharmony_ci			  DEINTERLACE_AGTH_SEL_LINEBUF);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	val = DEINTERLACE_CTRL_EN |
2638c2ecf20Sopenharmony_ci	      DEINTERLACE_CTRL_MODE_MIXED |
2648c2ecf20Sopenharmony_ci	      DEINTERLACE_CTRL_DIAG_INTP_EN |
2658c2ecf20Sopenharmony_ci	      DEINTERLACE_CTRL_TEMP_DIFF_EN;
2668c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_CTRL, val);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_LUMA_TH,
2698c2ecf20Sopenharmony_ci				 DEINTERLACE_LUMA_TH_MIN_LUMA_MSK,
2708c2ecf20Sopenharmony_ci				 DEINTERLACE_LUMA_TH_MIN_LUMA(4));
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_SPAT_COMP,
2738c2ecf20Sopenharmony_ci				 DEINTERLACE_SPAT_COMP_TH2_MSK,
2748c2ecf20Sopenharmony_ci				 DEINTERLACE_SPAT_COMP_TH2(5));
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_TEMP_DIFF,
2778c2ecf20Sopenharmony_ci				 DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK,
2788c2ecf20Sopenharmony_ci				 DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(5));
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	val = DEINTERLACE_DIAG_INTP_TH0(60) |
2818c2ecf20Sopenharmony_ci	      DEINTERLACE_DIAG_INTP_TH1(0) |
2828c2ecf20Sopenharmony_ci	      DEINTERLACE_DIAG_INTP_TH3(30);
2838c2ecf20Sopenharmony_ci	deinterlace_write(dev, DEINTERLACE_DIAG_INTP, val);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_CHROMA_DIFF,
2868c2ecf20Sopenharmony_ci				 DEINTERLACE_CHROMA_DIFF_TH_MSK,
2878c2ecf20Sopenharmony_ci				 DEINTERLACE_CHROMA_DIFF_TH(5));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* neutral filter coefficients */
2908c2ecf20Sopenharmony_ci	deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL,
2918c2ecf20Sopenharmony_ci			     DEINTERLACE_FRM_CTRL_COEF_ACCESS);
2928c2ecf20Sopenharmony_ci	readl_poll_timeout(dev->base + DEINTERLACE_STATUS, val,
2938c2ecf20Sopenharmony_ci			   val & DEINTERLACE_STATUS_COEF_STATUS, 2, 40);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++) {
2968c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH0_HORZ_COEF0 + i * 4,
2978c2ecf20Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
2988c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH0_VERT_COEF + i * 4,
2998c2ecf20Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
3008c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH1_HORZ_COEF0 + i * 4,
3018c2ecf20Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
3028c2ecf20Sopenharmony_ci		deinterlace_write(dev, DEINTERLACE_CH1_VERT_COEF + i * 4,
3038c2ecf20Sopenharmony_ci				  DEINTERLACE_IDENTITY_COEF);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL,
3078c2ecf20Sopenharmony_ci				 DEINTERLACE_FRM_CTRL_COEF_ACCESS, 0);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	return container_of(file->private_data, struct deinterlace_ctx, fh);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic bool deinterlace_check_format(u32 pixelformat)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	unsigned int i;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++)
3208c2ecf20Sopenharmony_ci		if (deinterlace_formats[i] == pixelformat)
3218c2ecf20Sopenharmony_ci			return true;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return false;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	unsigned int height = pix_fmt->height;
3298c2ecf20Sopenharmony_ci	unsigned int width = pix_fmt->width;
3308c2ecf20Sopenharmony_ci	unsigned int bytesperline;
3318c2ecf20Sopenharmony_ci	unsigned int sizeimage;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	width = clamp(width, DEINTERLACE_MIN_WIDTH,
3348c2ecf20Sopenharmony_ci		      DEINTERLACE_MAX_WIDTH);
3358c2ecf20Sopenharmony_ci	height = clamp(height, DEINTERLACE_MIN_HEIGHT,
3368c2ecf20Sopenharmony_ci		       DEINTERLACE_MAX_HEIGHT);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	bytesperline = ALIGN(width, 2);
3398c2ecf20Sopenharmony_ci	/* luma */
3408c2ecf20Sopenharmony_ci	sizeimage = bytesperline * height;
3418c2ecf20Sopenharmony_ci	/* chroma */
3428c2ecf20Sopenharmony_ci	sizeimage += bytesperline * height / 2;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	pix_fmt->width = width;
3458c2ecf20Sopenharmony_ci	pix_fmt->height = height;
3468c2ecf20Sopenharmony_ci	pix_fmt->bytesperline = bytesperline;
3478c2ecf20Sopenharmony_ci	pix_fmt->sizeimage = sizeimage;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int deinterlace_querycap(struct file *file, void *priv,
3518c2ecf20Sopenharmony_ci				struct v4l2_capability *cap)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver));
3548c2ecf20Sopenharmony_ci	strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card));
3558c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
3568c2ecf20Sopenharmony_ci		 "platform:%s", DEINTERLACE_NAME);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int deinterlace_enum_fmt(struct file *file, void *priv,
3628c2ecf20Sopenharmony_ci				struct v4l2_fmtdesc *f)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	if (f->index < ARRAY_SIZE(deinterlace_formats)) {
3658c2ecf20Sopenharmony_ci		f->pixelformat = deinterlace_formats[f->index];
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		return 0;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return -EINVAL;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int deinterlace_enum_framesizes(struct file *file, void *priv,
3748c2ecf20Sopenharmony_ci				       struct v4l2_frmsizeenum *fsize)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	if (fsize->index != 0)
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (!deinterlace_check_format(fsize->pixel_format))
3808c2ecf20Sopenharmony_ci		return -EINVAL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
3838c2ecf20Sopenharmony_ci	fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH;
3848c2ecf20Sopenharmony_ci	fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT;
3858c2ecf20Sopenharmony_ci	fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH;
3868c2ecf20Sopenharmony_ci	fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT;
3878c2ecf20Sopenharmony_ci	fsize->stepwise.step_width = 2;
3888c2ecf20Sopenharmony_ci	fsize->stepwise.step_height = 1;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int deinterlace_g_fmt_vid_cap(struct file *file, void *priv,
3948c2ecf20Sopenharmony_ci				     struct v4l2_format *f)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	f->fmt.pix = ctx->dst_fmt;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int deinterlace_g_fmt_vid_out(struct file *file, void *priv,
4048c2ecf20Sopenharmony_ci				     struct v4l2_format *f)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	f->fmt.pix = ctx->src_fmt;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int deinterlace_try_fmt_vid_cap(struct file *file, void *priv,
4148c2ecf20Sopenharmony_ci				       struct v4l2_format *f)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	if (!deinterlace_check_format(f->fmt.pix.pixelformat))
4178c2ecf20Sopenharmony_ci		f->fmt.pix.pixelformat = deinterlace_formats[0];
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (f->fmt.pix.field != V4L2_FIELD_NONE)
4208c2ecf20Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_NONE;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	deinterlace_prepare_format(&f->fmt.pix);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return 0;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int deinterlace_try_fmt_vid_out(struct file *file, void *priv,
4288c2ecf20Sopenharmony_ci				       struct v4l2_format *f)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	if (!deinterlace_check_format(f->fmt.pix.pixelformat))
4318c2ecf20Sopenharmony_ci		f->fmt.pix.pixelformat = deinterlace_formats[0];
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
4348c2ecf20Sopenharmony_ci	    f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
4358c2ecf20Sopenharmony_ci	    f->fmt.pix.field != V4L2_FIELD_INTERLACED)
4368c2ecf20Sopenharmony_ci		f->fmt.pix.field = V4L2_FIELD_INTERLACED;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	deinterlace_prepare_format(&f->fmt.pix);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int deinterlace_s_fmt_vid_cap(struct file *file, void *priv,
4448c2ecf20Sopenharmony_ci				     struct v4l2_format *f)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
4478c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
4488c2ecf20Sopenharmony_ci	int ret;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	ret = deinterlace_try_fmt_vid_cap(file, priv, f);
4518c2ecf20Sopenharmony_ci	if (ret)
4528c2ecf20Sopenharmony_ci		return ret;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
4558c2ecf20Sopenharmony_ci	if (vb2_is_busy(vq))
4568c2ecf20Sopenharmony_ci		return -EBUSY;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	ctx->dst_fmt = f->fmt.pix;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return 0;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int deinterlace_s_fmt_vid_out(struct file *file, void *priv,
4648c2ecf20Sopenharmony_ci				     struct v4l2_format *f)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = deinterlace_file2ctx(file);
4678c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
4688c2ecf20Sopenharmony_ci	int ret;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	ret = deinterlace_try_fmt_vid_out(file, priv, f);
4718c2ecf20Sopenharmony_ci	if (ret)
4728c2ecf20Sopenharmony_ci		return ret;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
4758c2ecf20Sopenharmony_ci	if (vb2_is_busy(vq))
4768c2ecf20Sopenharmony_ci		return -EBUSY;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	ctx->src_fmt = f->fmt.pix;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/* Propagate colorspace information to capture. */
4818c2ecf20Sopenharmony_ci	ctx->dst_fmt.colorspace = f->fmt.pix.colorspace;
4828c2ecf20Sopenharmony_ci	ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func;
4838c2ecf20Sopenharmony_ci	ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
4848c2ecf20Sopenharmony_ci	ctx->dst_fmt.quantization = f->fmt.pix.quantization;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
4908c2ecf20Sopenharmony_ci	.vidioc_querycap		= deinterlace_querycap,
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	.vidioc_enum_framesizes		= deinterlace_enum_framesizes,
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= deinterlace_enum_fmt,
4958c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= deinterlace_g_fmt_vid_cap,
4968c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= deinterlace_try_fmt_vid_cap,
4978c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= deinterlace_s_fmt_vid_cap,
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= deinterlace_enum_fmt,
5008c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_out		= deinterlace_g_fmt_vid_out,
5018c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_out		= deinterlace_try_fmt_vid_out,
5028c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_out		= deinterlace_s_fmt_vid_out,
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
5058c2ecf20Sopenharmony_ci	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
5068c2ecf20Sopenharmony_ci	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
5078c2ecf20Sopenharmony_ci	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
5088c2ecf20Sopenharmony_ci	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
5098c2ecf20Sopenharmony_ci	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
5108c2ecf20Sopenharmony_ci	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
5138c2ecf20Sopenharmony_ci	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
5148c2ecf20Sopenharmony_ci};
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
5178c2ecf20Sopenharmony_ci				   unsigned int *nplanes, unsigned int sizes[],
5188c2ecf20Sopenharmony_ci				   struct device *alloc_devs[])
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
5218c2ecf20Sopenharmony_ci	struct v4l2_pix_format *pix_fmt;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type))
5248c2ecf20Sopenharmony_ci		pix_fmt = &ctx->src_fmt;
5258c2ecf20Sopenharmony_ci	else
5268c2ecf20Sopenharmony_ci		pix_fmt = &ctx->dst_fmt;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (*nplanes) {
5298c2ecf20Sopenharmony_ci		if (sizes[0] < pix_fmt->sizeimage)
5308c2ecf20Sopenharmony_ci			return -EINVAL;
5318c2ecf20Sopenharmony_ci	} else {
5328c2ecf20Sopenharmony_ci		sizes[0] = pix_fmt->sizeimage;
5338c2ecf20Sopenharmony_ci		*nplanes = 1;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int deinterlace_buf_prepare(struct vb2_buffer *vb)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
5428c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
5438c2ecf20Sopenharmony_ci	struct v4l2_pix_format *pix_fmt;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type))
5468c2ecf20Sopenharmony_ci		pix_fmt = &ctx->src_fmt;
5478c2ecf20Sopenharmony_ci	else
5488c2ecf20Sopenharmony_ci		pix_fmt = &ctx->dst_fmt;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
5518c2ecf20Sopenharmony_ci		return -EINVAL;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic void deinterlace_buf_queue(struct vb2_buffer *vb)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
5618c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
5698c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	do {
5728c2ecf20Sopenharmony_ci		if (V4L2_TYPE_IS_OUTPUT(vq->type))
5738c2ecf20Sopenharmony_ci			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
5748c2ecf20Sopenharmony_ci		else
5758c2ecf20Sopenharmony_ci			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		if (vbuf)
5788c2ecf20Sopenharmony_ci			v4l2_m2m_buf_done(vbuf, state);
5798c2ecf20Sopenharmony_ci	} while (vbuf);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev)
5828c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(ctx->prev, state);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
5888c2ecf20Sopenharmony_ci	struct device *dev = ctx->dev->dev;
5898c2ecf20Sopenharmony_ci	int ret;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
5928c2ecf20Sopenharmony_ci		ret = pm_runtime_resume_and_get(dev);
5938c2ecf20Sopenharmony_ci		if (ret < 0) {
5948c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to enable module\n");
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci			goto err_runtime_get;
5978c2ecf20Sopenharmony_ci		}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		ctx->first_field =
6008c2ecf20Sopenharmony_ci			ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT;
6018c2ecf20Sopenharmony_ci		ctx->field = ctx->first_field;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		ctx->prev = NULL;
6048c2ecf20Sopenharmony_ci		ctx->aborting = 0;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE,
6078c2ecf20Sopenharmony_ci						    &ctx->flag1_buf_dma,
6088c2ecf20Sopenharmony_ci						    GFP_KERNEL);
6098c2ecf20Sopenharmony_ci		if (!ctx->flag1_buf) {
6108c2ecf20Sopenharmony_ci			ret = -ENOMEM;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci			goto err_no_mem1;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE,
6168c2ecf20Sopenharmony_ci						    &ctx->flag2_buf_dma,
6178c2ecf20Sopenharmony_ci						    GFP_KERNEL);
6188c2ecf20Sopenharmony_ci		if (!ctx->flag2_buf) {
6198c2ecf20Sopenharmony_ci			ret = -ENOMEM;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci			goto err_no_mem2;
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cierr_no_mem2:
6288c2ecf20Sopenharmony_ci	dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
6298c2ecf20Sopenharmony_ci			  ctx->flag1_buf_dma);
6308c2ecf20Sopenharmony_cierr_no_mem1:
6318c2ecf20Sopenharmony_ci	pm_runtime_put(dev);
6328c2ecf20Sopenharmony_cierr_runtime_get:
6338c2ecf20Sopenharmony_ci	deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return ret;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void deinterlace_stop_streaming(struct vb2_queue *vq)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
6438c2ecf20Sopenharmony_ci		struct device *dev = ctx->dev->dev;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf,
6468c2ecf20Sopenharmony_ci				  ctx->flag1_buf_dma);
6478c2ecf20Sopenharmony_ci		dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf,
6488c2ecf20Sopenharmony_ci				  ctx->flag2_buf_dma);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		pm_runtime_put(dev);
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic const struct vb2_ops deinterlace_qops = {
6578c2ecf20Sopenharmony_ci	.queue_setup		= deinterlace_queue_setup,
6588c2ecf20Sopenharmony_ci	.buf_prepare		= deinterlace_buf_prepare,
6598c2ecf20Sopenharmony_ci	.buf_queue		= deinterlace_buf_queue,
6608c2ecf20Sopenharmony_ci	.start_streaming	= deinterlace_start_streaming,
6618c2ecf20Sopenharmony_ci	.stop_streaming		= deinterlace_stop_streaming,
6628c2ecf20Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
6638c2ecf20Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
6648c2ecf20Sopenharmony_ci};
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq,
6678c2ecf20Sopenharmony_ci				  struct vb2_queue *dst_vq)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = priv;
6708c2ecf20Sopenharmony_ci	int ret;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
6738c2ecf20Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
6748c2ecf20Sopenharmony_ci	src_vq->drv_priv = ctx;
6758c2ecf20Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
6768c2ecf20Sopenharmony_ci	src_vq->min_buffers_needed = 1;
6778c2ecf20Sopenharmony_ci	src_vq->ops = &deinterlace_qops;
6788c2ecf20Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
6798c2ecf20Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
6808c2ecf20Sopenharmony_ci	src_vq->lock = &ctx->dev->dev_mutex;
6818c2ecf20Sopenharmony_ci	src_vq->dev = ctx->dev->dev;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	ret = vb2_queue_init(src_vq);
6848c2ecf20Sopenharmony_ci	if (ret)
6858c2ecf20Sopenharmony_ci		return ret;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
6888c2ecf20Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
6898c2ecf20Sopenharmony_ci	dst_vq->drv_priv = ctx;
6908c2ecf20Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
6918c2ecf20Sopenharmony_ci	dst_vq->min_buffers_needed = 2;
6928c2ecf20Sopenharmony_ci	dst_vq->ops = &deinterlace_qops;
6938c2ecf20Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
6948c2ecf20Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
6958c2ecf20Sopenharmony_ci	dst_vq->lock = &ctx->dev->dev_mutex;
6968c2ecf20Sopenharmony_ci	dst_vq->dev = ctx->dev->dev;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	ret = vb2_queue_init(dst_vq);
6998c2ecf20Sopenharmony_ci	if (ret)
7008c2ecf20Sopenharmony_ci		return ret;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	return 0;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic int deinterlace_open(struct file *file)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = video_drvdata(file);
7088c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = NULL;
7098c2ecf20Sopenharmony_ci	int ret;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&dev->dev_mutex))
7128c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
7158c2ecf20Sopenharmony_ci	if (!ctx) {
7168c2ecf20Sopenharmony_ci		mutex_unlock(&dev->dev_mutex);
7178c2ecf20Sopenharmony_ci		return -ENOMEM;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* default output format */
7218c2ecf20Sopenharmony_ci	ctx->src_fmt.pixelformat = deinterlace_formats[0];
7228c2ecf20Sopenharmony_ci	ctx->src_fmt.field = V4L2_FIELD_INTERLACED;
7238c2ecf20Sopenharmony_ci	ctx->src_fmt.width = 640;
7248c2ecf20Sopenharmony_ci	ctx->src_fmt.height = 480;
7258c2ecf20Sopenharmony_ci	deinterlace_prepare_format(&ctx->src_fmt);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* default capture format */
7288c2ecf20Sopenharmony_ci	ctx->dst_fmt.pixelformat = deinterlace_formats[0];
7298c2ecf20Sopenharmony_ci	ctx->dst_fmt.field = V4L2_FIELD_NONE;
7308c2ecf20Sopenharmony_ci	ctx->dst_fmt.width = 640;
7318c2ecf20Sopenharmony_ci	ctx->dst_fmt.height = 480;
7328c2ecf20Sopenharmony_ci	deinterlace_prepare_format(&ctx->dst_fmt);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	v4l2_fh_init(&ctx->fh, video_devdata(file));
7358c2ecf20Sopenharmony_ci	file->private_data = &ctx->fh;
7368c2ecf20Sopenharmony_ci	ctx->dev = dev;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
7398c2ecf20Sopenharmony_ci					    &deinterlace_queue_init);
7408c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->fh.m2m_ctx)) {
7418c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctx->fh.m2m_ctx);
7428c2ecf20Sopenharmony_ci		goto err_free;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	return 0;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cierr_free:
7528c2ecf20Sopenharmony_ci	kfree(ctx);
7538c2ecf20Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	return ret;
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_cistatic int deinterlace_release(struct file *file)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = video_drvdata(file);
7618c2ecf20Sopenharmony_ci	struct deinterlace_ctx *ctx = container_of(file->private_data,
7628c2ecf20Sopenharmony_ci						   struct deinterlace_ctx, fh);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
7678c2ecf20Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
7688c2ecf20Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	kfree(ctx);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	return 0;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations deinterlace_fops = {
7788c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7798c2ecf20Sopenharmony_ci	.open		= deinterlace_open,
7808c2ecf20Sopenharmony_ci	.release	= deinterlace_release,
7818c2ecf20Sopenharmony_ci	.poll		= v4l2_m2m_fop_poll,
7828c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
7838c2ecf20Sopenharmony_ci	.mmap		= v4l2_m2m_fop_mmap,
7848c2ecf20Sopenharmony_ci};
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic const struct video_device deinterlace_video_device = {
7878c2ecf20Sopenharmony_ci	.name		= DEINTERLACE_NAME,
7888c2ecf20Sopenharmony_ci	.vfl_dir	= VFL_DIR_M2M,
7898c2ecf20Sopenharmony_ci	.fops		= &deinterlace_fops,
7908c2ecf20Sopenharmony_ci	.ioctl_ops	= &deinterlace_ioctl_ops,
7918c2ecf20Sopenharmony_ci	.minor		= -1,
7928c2ecf20Sopenharmony_ci	.release	= video_device_release_empty,
7938c2ecf20Sopenharmony_ci	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
7948c2ecf20Sopenharmony_ci};
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops deinterlace_m2m_ops = {
7978c2ecf20Sopenharmony_ci	.device_run	= deinterlace_device_run,
7988c2ecf20Sopenharmony_ci	.job_ready	= deinterlace_job_ready,
7998c2ecf20Sopenharmony_ci	.job_abort	= deinterlace_job_abort,
8008c2ecf20Sopenharmony_ci};
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int deinterlace_probe(struct platform_device *pdev)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev;
8058c2ecf20Sopenharmony_ci	struct video_device *vfd;
8068c2ecf20Sopenharmony_ci	struct resource *res;
8078c2ecf20Sopenharmony_ci	int irq, ret;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
8108c2ecf20Sopenharmony_ci	if (!dev)
8118c2ecf20Sopenharmony_ci		return -ENOMEM;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	dev->vfd = deinterlace_video_device;
8148c2ecf20Sopenharmony_ci	dev->dev = &pdev->dev;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
8178c2ecf20Sopenharmony_ci	if (irq <= 0)
8188c2ecf20Sopenharmony_ci		return irq;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev->dev, irq, deinterlace_irq,
8218c2ecf20Sopenharmony_ci			       0, dev_name(dev->dev), dev);
8228c2ecf20Sopenharmony_ci	if (ret) {
8238c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to request IRQ\n");
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		return ret;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	ret = of_dma_configure(dev->dev, dev->dev->of_node, true);
8298c2ecf20Sopenharmony_ci	if (ret)
8308c2ecf20Sopenharmony_ci		return ret;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8338c2ecf20Sopenharmony_ci	dev->base = devm_ioremap_resource(&pdev->dev, res);
8348c2ecf20Sopenharmony_ci	if (IS_ERR(dev->base))
8358c2ecf20Sopenharmony_ci		return PTR_ERR(dev->base);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	dev->bus_clk = devm_clk_get(dev->dev, "bus");
8388c2ecf20Sopenharmony_ci	if (IS_ERR(dev->bus_clk)) {
8398c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to get bus clock\n");
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		return PTR_ERR(dev->bus_clk);
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	dev->mod_clk = devm_clk_get(dev->dev, "mod");
8458c2ecf20Sopenharmony_ci	if (IS_ERR(dev->mod_clk)) {
8468c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to get mod clock\n");
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		return PTR_ERR(dev->mod_clk);
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	dev->ram_clk = devm_clk_get(dev->dev, "ram");
8528c2ecf20Sopenharmony_ci	if (IS_ERR(dev->ram_clk)) {
8538c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to get ram clock\n");
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		return PTR_ERR(dev->ram_clk);
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	dev->rstc = devm_reset_control_get(dev->dev, NULL);
8598c2ecf20Sopenharmony_ci	if (IS_ERR(dev->rstc)) {
8608c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to get reset control\n");
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		return PTR_ERR(dev->rstc);
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	mutex_init(&dev->dev_mutex);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
8688c2ecf20Sopenharmony_ci	if (ret) {
8698c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to register V4L2 device\n");
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci		return ret;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	vfd = &dev->vfd;
8758c2ecf20Sopenharmony_ci	vfd->lock = &dev->dev_mutex;
8768c2ecf20Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s",
8798c2ecf20Sopenharmony_ci		 deinterlace_video_device.name);
8808c2ecf20Sopenharmony_ci	video_set_drvdata(vfd, dev);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
8838c2ecf20Sopenharmony_ci	if (ret) {
8848c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		goto err_v4l2;
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	v4l2_info(&dev->v4l2_dev,
8908c2ecf20Sopenharmony_ci		  "Device registered as /dev/video%d\n", vfd->num);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops);
8938c2ecf20Sopenharmony_ci	if (IS_ERR(dev->m2m_dev)) {
8948c2ecf20Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
8958c2ecf20Sopenharmony_ci			 "Failed to initialize V4L2 M2M device\n");
8968c2ecf20Sopenharmony_ci		ret = PTR_ERR(dev->m2m_dev);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		goto err_video;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dev);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	pm_runtime_enable(dev->dev);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return 0;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cierr_video:
9088c2ecf20Sopenharmony_ci	video_unregister_device(&dev->vfd);
9098c2ecf20Sopenharmony_cierr_v4l2:
9108c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return ret;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic int deinterlace_remove(struct platform_device *pdev)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = platform_get_drvdata(pdev);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	v4l2_m2m_release(dev->m2m_dev);
9208c2ecf20Sopenharmony_ci	video_unregister_device(&dev->vfd);
9218c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	pm_runtime_force_suspend(&pdev->dev);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	return 0;
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_cistatic int deinterlace_runtime_resume(struct device *device)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = dev_get_drvdata(device);
9318c2ecf20Sopenharmony_ci	int ret;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	ret = clk_set_rate_exclusive(dev->mod_clk, 300000000);
9348c2ecf20Sopenharmony_ci	if (ret) {
9358c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to set exclusive mod clock rate\n");
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		return ret;
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dev->bus_clk);
9418c2ecf20Sopenharmony_ci	if (ret) {
9428c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to enable bus clock\n");
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		goto err_exclusive_rate;
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dev->mod_clk);
9488c2ecf20Sopenharmony_ci	if (ret) {
9498c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to enable mod clock\n");
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		goto err_bus_clk;
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dev->ram_clk);
9558c2ecf20Sopenharmony_ci	if (ret) {
9568c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to enable ram clock\n");
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		goto err_mod_clk;
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	ret = reset_control_deassert(dev->rstc);
9628c2ecf20Sopenharmony_ci	if (ret) {
9638c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Failed to apply reset\n");
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		goto err_ram_clk;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	deinterlace_init(dev);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return 0;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cierr_ram_clk:
9738c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->ram_clk);
9748c2ecf20Sopenharmony_cierr_mod_clk:
9758c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->mod_clk);
9768c2ecf20Sopenharmony_cierr_bus_clk:
9778c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->bus_clk);
9788c2ecf20Sopenharmony_cierr_exclusive_rate:
9798c2ecf20Sopenharmony_ci	clk_rate_exclusive_put(dev->mod_clk);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return ret;
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic int deinterlace_runtime_suspend(struct device *device)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	struct deinterlace_dev *dev = dev_get_drvdata(device);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	reset_control_assert(dev->rstc);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->ram_clk);
9918c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->mod_clk);
9928c2ecf20Sopenharmony_ci	clk_disable_unprepare(dev->bus_clk);
9938c2ecf20Sopenharmony_ci	clk_rate_exclusive_put(dev->mod_clk);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return 0;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_cistatic const struct of_device_id deinterlace_dt_match[] = {
9998c2ecf20Sopenharmony_ci	{ .compatible = "allwinner,sun8i-h3-deinterlace" },
10008c2ecf20Sopenharmony_ci	{ /* sentinel */ }
10018c2ecf20Sopenharmony_ci};
10028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, deinterlace_dt_match);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic const struct dev_pm_ops deinterlace_pm_ops = {
10058c2ecf20Sopenharmony_ci	.runtime_resume		= deinterlace_runtime_resume,
10068c2ecf20Sopenharmony_ci	.runtime_suspend	= deinterlace_runtime_suspend,
10078c2ecf20Sopenharmony_ci};
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cistatic struct platform_driver deinterlace_driver = {
10108c2ecf20Sopenharmony_ci	.probe		= deinterlace_probe,
10118c2ecf20Sopenharmony_ci	.remove		= deinterlace_remove,
10128c2ecf20Sopenharmony_ci	.driver		= {
10138c2ecf20Sopenharmony_ci		.name		= DEINTERLACE_NAME,
10148c2ecf20Sopenharmony_ci		.of_match_table	= deinterlace_dt_match,
10158c2ecf20Sopenharmony_ci		.pm		= &deinterlace_pm_ops,
10168c2ecf20Sopenharmony_ci	},
10178c2ecf20Sopenharmony_ci};
10188c2ecf20Sopenharmony_cimodule_platform_driver(deinterlace_driver);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
10218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
10228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner Deinterlace driver");
1023