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