1a4260ea4SJernej Skrabec // SPDX-License-Identifier: GPL-2.0 2a4260ea4SJernej Skrabec /* 3a4260ea4SJernej Skrabec * Allwinner sun8i deinterlacer with scaler driver 4a4260ea4SJernej Skrabec * 5a4260ea4SJernej Skrabec * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net> 6a4260ea4SJernej Skrabec * 7a4260ea4SJernej Skrabec * Based on vim2m driver. 8a4260ea4SJernej Skrabec */ 9a4260ea4SJernej Skrabec 10a4260ea4SJernej Skrabec #include <linux/clk.h> 11a4260ea4SJernej Skrabec #include <linux/interrupt.h> 12a4260ea4SJernej Skrabec #include <linux/io.h> 13a4260ea4SJernej Skrabec #include <linux/iopoll.h> 14a4260ea4SJernej Skrabec #include <linux/module.h> 15a4260ea4SJernej Skrabec #include <linux/of.h> 16a4260ea4SJernej Skrabec #include <linux/of_device.h> 17a4260ea4SJernej Skrabec #include <linux/pm_runtime.h> 18a4260ea4SJernej Skrabec #include <linux/reset.h> 19a4260ea4SJernej Skrabec 20a4260ea4SJernej Skrabec #include <media/v4l2-device.h> 21a4260ea4SJernej Skrabec #include <media/v4l2-ioctl.h> 22a4260ea4SJernej Skrabec #include <media/v4l2-mem2mem.h> 23a4260ea4SJernej Skrabec 24a4260ea4SJernej Skrabec #include "sun8i-di.h" 25a4260ea4SJernej Skrabec 26a4260ea4SJernej Skrabec #define FLAG_SIZE (DEINTERLACE_MAX_WIDTH * DEINTERLACE_MAX_HEIGHT / 4) 27a4260ea4SJernej Skrabec 28a4260ea4SJernej Skrabec static u32 deinterlace_formats[] = { 29a4260ea4SJernej Skrabec V4L2_PIX_FMT_NV12, 30a4260ea4SJernej Skrabec V4L2_PIX_FMT_NV21, 31a4260ea4SJernej Skrabec }; 32a4260ea4SJernej Skrabec 33a4260ea4SJernej Skrabec static inline u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg) 34a4260ea4SJernej Skrabec { 35a4260ea4SJernej Skrabec return readl(dev->base + reg); 36a4260ea4SJernej Skrabec } 37a4260ea4SJernej Skrabec 38a4260ea4SJernej Skrabec static inline void deinterlace_write(struct deinterlace_dev *dev, 39a4260ea4SJernej Skrabec u32 reg, u32 value) 40a4260ea4SJernej Skrabec { 41a4260ea4SJernej Skrabec writel(value, dev->base + reg); 42a4260ea4SJernej Skrabec } 43a4260ea4SJernej Skrabec 44a4260ea4SJernej Skrabec static inline void deinterlace_set_bits(struct deinterlace_dev *dev, 45a4260ea4SJernej Skrabec u32 reg, u32 bits) 46a4260ea4SJernej Skrabec { 47a4260ea4SJernej Skrabec writel(readl(dev->base + reg) | bits, dev->base + reg); 48a4260ea4SJernej Skrabec } 49a4260ea4SJernej Skrabec 50a4260ea4SJernej Skrabec static inline void deinterlace_clr_set_bits(struct deinterlace_dev *dev, 51a4260ea4SJernej Skrabec u32 reg, u32 clr, u32 set) 52a4260ea4SJernej Skrabec { 53a4260ea4SJernej Skrabec u32 val = readl(dev->base + reg); 54a4260ea4SJernej Skrabec 55a4260ea4SJernej Skrabec val &= ~clr; 56a4260ea4SJernej Skrabec val |= set; 57a4260ea4SJernej Skrabec 58a4260ea4SJernej Skrabec writel(val, dev->base + reg); 59a4260ea4SJernej Skrabec } 60a4260ea4SJernej Skrabec 61a4260ea4SJernej Skrabec static void deinterlace_device_run(void *priv) 62a4260ea4SJernej Skrabec { 63a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = priv; 64a4260ea4SJernej Skrabec struct deinterlace_dev *dev = ctx->dev; 65a4260ea4SJernej Skrabec u32 size, stride, width, height, val; 66a4260ea4SJernej Skrabec struct vb2_v4l2_buffer *src, *dst; 67a4260ea4SJernej Skrabec unsigned int hstep, vstep; 68a4260ea4SJernej Skrabec dma_addr_t addr; 69a4260ea4SJernej Skrabec 70a4260ea4SJernej Skrabec src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 71a4260ea4SJernej Skrabec dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 72a4260ea4SJernej Skrabec 73a4260ea4SJernej Skrabec v4l2_m2m_buf_copy_metadata(src, dst, true); 74a4260ea4SJernej Skrabec 75a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 76a4260ea4SJernej Skrabec DEINTERLACE_MOD_ENABLE_EN); 77a4260ea4SJernej Skrabec 78a4260ea4SJernej Skrabec if (ctx->field) { 79a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_TILE_FLAG0, 80a4260ea4SJernej Skrabec ctx->flag1_buf_dma); 81a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_TILE_FLAG1, 82a4260ea4SJernej Skrabec ctx->flag2_buf_dma); 83a4260ea4SJernej Skrabec } else { 84a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_TILE_FLAG0, 85a4260ea4SJernej Skrabec ctx->flag2_buf_dma); 86a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_TILE_FLAG1, 87a4260ea4SJernej Skrabec ctx->flag1_buf_dma); 88a4260ea4SJernej Skrabec } 89a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_FLAG_LINE_STRIDE, 0x200); 90a4260ea4SJernej Skrabec 91a4260ea4SJernej Skrabec width = ctx->src_fmt.width; 92a4260ea4SJernej Skrabec height = ctx->src_fmt.height; 93a4260ea4SJernej Skrabec stride = ctx->src_fmt.bytesperline; 94a4260ea4SJernej Skrabec size = stride * height; 95a4260ea4SJernej Skrabec 96a4260ea4SJernej Skrabec addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0); 97a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_BUF_ADDR0, addr); 98a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_BUF_ADDR1, addr + size); 99a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_BUF_ADDR2, 0); 100a4260ea4SJernej Skrabec 101a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_LINE_STRIDE0, stride); 102a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_LINE_STRIDE1, stride); 103a4260ea4SJernej Skrabec 104a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_IN_SIZE, 105a4260ea4SJernej Skrabec DEINTERLACE_SIZE(width, height)); 106a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_IN_SIZE, 107a4260ea4SJernej Skrabec DEINTERLACE_SIZE(width / 2, height / 2)); 108a4260ea4SJernej Skrabec 109a4260ea4SJernej Skrabec val = DEINTERLACE_IN_FMT_FMT(DEINTERLACE_IN_FMT_YUV420) | 110a4260ea4SJernej Skrabec DEINTERLACE_IN_FMT_MOD(DEINTERLACE_MODE_UV_COMBINED); 111a4260ea4SJernej Skrabec switch (ctx->src_fmt.pixelformat) { 112a4260ea4SJernej Skrabec case V4L2_PIX_FMT_NV12: 113a4260ea4SJernej Skrabec val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_UVUV); 114a4260ea4SJernej Skrabec break; 115a4260ea4SJernej Skrabec case V4L2_PIX_FMT_NV21: 116a4260ea4SJernej Skrabec val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_VUVU); 117a4260ea4SJernej Skrabec break; 118a4260ea4SJernej Skrabec } 119a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_IN_FMT, val); 120a4260ea4SJernej Skrabec 121a4260ea4SJernej Skrabec if (ctx->prev) 122a4260ea4SJernej Skrabec addr = vb2_dma_contig_plane_dma_addr(&ctx->prev->vb2_buf, 0); 123a4260ea4SJernej Skrabec 124a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_PRELUMA, addr); 125a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_PRECHROMA, addr + size); 126a4260ea4SJernej Skrabec 127a4260ea4SJernej Skrabec val = DEINTERLACE_OUT_FMT_FMT(DEINTERLACE_OUT_FMT_YUV420SP); 128a4260ea4SJernej Skrabec switch (ctx->src_fmt.pixelformat) { 129a4260ea4SJernej Skrabec case V4L2_PIX_FMT_NV12: 130a4260ea4SJernej Skrabec val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_UVUV); 131a4260ea4SJernej Skrabec break; 132a4260ea4SJernej Skrabec case V4L2_PIX_FMT_NV21: 133a4260ea4SJernej Skrabec val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_VUVU); 134a4260ea4SJernej Skrabec break; 135a4260ea4SJernej Skrabec } 136a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_OUT_FMT, val); 137a4260ea4SJernej Skrabec 138a4260ea4SJernej Skrabec width = ctx->dst_fmt.width; 139a4260ea4SJernej Skrabec height = ctx->dst_fmt.height; 140a4260ea4SJernej Skrabec stride = ctx->dst_fmt.bytesperline; 141a4260ea4SJernej Skrabec size = stride * height; 142a4260ea4SJernej Skrabec 143a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_OUT_SIZE, 144a4260ea4SJernej Skrabec DEINTERLACE_SIZE(width, height)); 145a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_OUT_SIZE, 146a4260ea4SJernej Skrabec DEINTERLACE_SIZE(width / 2, height / 2)); 147a4260ea4SJernej Skrabec 148a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE0, stride); 149a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE1, stride); 150a4260ea4SJernej Skrabec 151a4260ea4SJernej Skrabec addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0); 152a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_ADDR0, addr); 153a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_ADDR1, addr + size); 154a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_ADDR2, 0); 155a4260ea4SJernej Skrabec 156a4260ea4SJernej Skrabec hstep = (ctx->src_fmt.width << 16) / ctx->dst_fmt.width; 157a4260ea4SJernej Skrabec vstep = (ctx->src_fmt.height << 16) / ctx->dst_fmt.height; 158a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_HORZ_FACT, hstep); 159a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_VERT_FACT, vstep); 160a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_HORZ_FACT, hstep); 161a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_VERT_FACT, vstep); 162a4260ea4SJernej Skrabec 163a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_FIELD_CTRL, 164a4260ea4SJernej Skrabec DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK, 165a4260ea4SJernej Skrabec DEINTERLACE_FIELD_CTRL_FIELD_CNT(ctx->field)); 166a4260ea4SJernej Skrabec 167a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, 168a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_START); 169a4260ea4SJernej Skrabec 170a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, 171a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_REG_READY); 172a4260ea4SJernej Skrabec 173a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_INT_ENABLE, 174a4260ea4SJernej Skrabec DEINTERLACE_INT_ENABLE_WB_EN); 175a4260ea4SJernej Skrabec 176a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, 177a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_WB_EN); 178a4260ea4SJernej Skrabec } 179a4260ea4SJernej Skrabec 180a4260ea4SJernej Skrabec static int deinterlace_job_ready(void *priv) 181a4260ea4SJernej Skrabec { 182a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = priv; 183a4260ea4SJernej Skrabec 184a4260ea4SJernej Skrabec return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 && 185a4260ea4SJernej Skrabec v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2; 186a4260ea4SJernej Skrabec } 187a4260ea4SJernej Skrabec 188a4260ea4SJernej Skrabec static void deinterlace_job_abort(void *priv) 189a4260ea4SJernej Skrabec { 190a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = priv; 191a4260ea4SJernej Skrabec 192a4260ea4SJernej Skrabec /* Will cancel the transaction in the next interrupt handler */ 193a4260ea4SJernej Skrabec ctx->aborting = 1; 194a4260ea4SJernej Skrabec } 195a4260ea4SJernej Skrabec 196a4260ea4SJernej Skrabec static irqreturn_t deinterlace_irq(int irq, void *data) 197a4260ea4SJernej Skrabec { 198a4260ea4SJernej Skrabec struct deinterlace_dev *dev = data; 199a4260ea4SJernej Skrabec struct vb2_v4l2_buffer *src, *dst; 200a4260ea4SJernej Skrabec enum vb2_buffer_state state; 201a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx; 202a4260ea4SJernej Skrabec unsigned int val; 203a4260ea4SJernej Skrabec 204a4260ea4SJernej Skrabec ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); 205a4260ea4SJernej Skrabec if (!ctx) { 206a4260ea4SJernej Skrabec v4l2_err(&dev->v4l2_dev, 207a4260ea4SJernej Skrabec "Instance released before the end of transaction\n"); 208a4260ea4SJernej Skrabec return IRQ_NONE; 209a4260ea4SJernej Skrabec } 210a4260ea4SJernej Skrabec 211a4260ea4SJernej Skrabec val = deinterlace_read(dev, DEINTERLACE_INT_STATUS); 212a4260ea4SJernej Skrabec if (!(val & DEINTERLACE_INT_STATUS_WRITEBACK)) 213a4260ea4SJernej Skrabec return IRQ_NONE; 214a4260ea4SJernej Skrabec 215a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_INT_ENABLE, 0); 216a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_INT_STATUS, 217a4260ea4SJernej Skrabec DEINTERLACE_INT_STATUS_WRITEBACK); 218a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 0); 219a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL, 220a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_START, 0); 221a4260ea4SJernej Skrabec 222a4260ea4SJernej Skrabec val = deinterlace_read(dev, DEINTERLACE_STATUS); 223a4260ea4SJernej Skrabec if (val & DEINTERLACE_STATUS_WB_ERROR) 224a4260ea4SJernej Skrabec state = VB2_BUF_STATE_ERROR; 225a4260ea4SJernej Skrabec else 226a4260ea4SJernej Skrabec state = VB2_BUF_STATE_DONE; 227a4260ea4SJernej Skrabec 228a4260ea4SJernej Skrabec dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 229a4260ea4SJernej Skrabec v4l2_m2m_buf_done(dst, state); 230a4260ea4SJernej Skrabec 231a4260ea4SJernej Skrabec if (ctx->field != ctx->first_field || ctx->aborting) { 232a4260ea4SJernej Skrabec ctx->field = ctx->first_field; 233a4260ea4SJernej Skrabec 234a4260ea4SJernej Skrabec src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 235a4260ea4SJernej Skrabec if (ctx->prev) 236a4260ea4SJernej Skrabec v4l2_m2m_buf_done(ctx->prev, state); 237a4260ea4SJernej Skrabec ctx->prev = src; 238a4260ea4SJernej Skrabec 239a4260ea4SJernej Skrabec v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); 240a4260ea4SJernej Skrabec } else { 241a4260ea4SJernej Skrabec ctx->field = !ctx->first_field; 242a4260ea4SJernej Skrabec deinterlace_device_run(ctx); 243a4260ea4SJernej Skrabec } 244a4260ea4SJernej Skrabec 245a4260ea4SJernej Skrabec return IRQ_HANDLED; 246a4260ea4SJernej Skrabec } 247a4260ea4SJernej Skrabec 248a4260ea4SJernej Skrabec static void deinterlace_init(struct deinterlace_dev *dev) 249a4260ea4SJernej Skrabec { 250a4260ea4SJernej Skrabec u32 val; 251a4260ea4SJernej Skrabec int i; 252a4260ea4SJernej Skrabec 253a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_BYPASS, 254a4260ea4SJernej Skrabec DEINTERLACE_BYPASS_CSC); 255a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE_CTRL, 256a4260ea4SJernej Skrabec DEINTERLACE_WB_LINE_STRIDE_CTRL_EN); 257a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, 258a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_OUT_CTRL); 259a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_AGTH_SEL, 260a4260ea4SJernej Skrabec DEINTERLACE_AGTH_SEL_LINEBUF); 261a4260ea4SJernej Skrabec 262a4260ea4SJernej Skrabec val = DEINTERLACE_CTRL_EN | 263a4260ea4SJernej Skrabec DEINTERLACE_CTRL_MODE_MIXED | 264a4260ea4SJernej Skrabec DEINTERLACE_CTRL_DIAG_INTP_EN | 265a4260ea4SJernej Skrabec DEINTERLACE_CTRL_TEMP_DIFF_EN; 266a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CTRL, val); 267a4260ea4SJernej Skrabec 268a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_LUMA_TH, 269a4260ea4SJernej Skrabec DEINTERLACE_LUMA_TH_MIN_LUMA_MSK, 270a4260ea4SJernej Skrabec DEINTERLACE_LUMA_TH_MIN_LUMA(4)); 271a4260ea4SJernej Skrabec 272a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_SPAT_COMP, 273a4260ea4SJernej Skrabec DEINTERLACE_SPAT_COMP_TH2_MSK, 274a4260ea4SJernej Skrabec DEINTERLACE_SPAT_COMP_TH2(5)); 275a4260ea4SJernej Skrabec 276a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_TEMP_DIFF, 277a4260ea4SJernej Skrabec DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK, 278a4260ea4SJernej Skrabec DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(5)); 279a4260ea4SJernej Skrabec 280a4260ea4SJernej Skrabec val = DEINTERLACE_DIAG_INTP_TH0(60) | 281a4260ea4SJernej Skrabec DEINTERLACE_DIAG_INTP_TH1(0) | 282a4260ea4SJernej Skrabec DEINTERLACE_DIAG_INTP_TH3(30); 283a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_DIAG_INTP, val); 284a4260ea4SJernej Skrabec 285a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_CHROMA_DIFF, 286a4260ea4SJernej Skrabec DEINTERLACE_CHROMA_DIFF_TH_MSK, 287a4260ea4SJernej Skrabec DEINTERLACE_CHROMA_DIFF_TH(5)); 288a4260ea4SJernej Skrabec 289a4260ea4SJernej Skrabec /* neutral filter coefficients */ 290a4260ea4SJernej Skrabec deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, 291a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_COEF_ACCESS); 292a4260ea4SJernej Skrabec readl_poll_timeout(dev->base + DEINTERLACE_STATUS, val, 293a4260ea4SJernej Skrabec val & DEINTERLACE_STATUS_COEF_STATUS, 2, 40); 294a4260ea4SJernej Skrabec 295a4260ea4SJernej Skrabec for (i = 0; i < 32; i++) { 296a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_HORZ_COEF0 + i * 4, 297a4260ea4SJernej Skrabec DEINTERLACE_IDENTITY_COEF); 298a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH0_VERT_COEF + i * 4, 299a4260ea4SJernej Skrabec DEINTERLACE_IDENTITY_COEF); 300a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_HORZ_COEF0 + i * 4, 301a4260ea4SJernej Skrabec DEINTERLACE_IDENTITY_COEF); 302a4260ea4SJernej Skrabec deinterlace_write(dev, DEINTERLACE_CH1_VERT_COEF + i * 4, 303a4260ea4SJernej Skrabec DEINTERLACE_IDENTITY_COEF); 304a4260ea4SJernej Skrabec } 305a4260ea4SJernej Skrabec 306a4260ea4SJernej Skrabec deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL, 307a4260ea4SJernej Skrabec DEINTERLACE_FRM_CTRL_COEF_ACCESS, 0); 308a4260ea4SJernej Skrabec } 309a4260ea4SJernej Skrabec 310a4260ea4SJernej Skrabec static inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file) 311a4260ea4SJernej Skrabec { 312a4260ea4SJernej Skrabec return container_of(file->private_data, struct deinterlace_ctx, fh); 313a4260ea4SJernej Skrabec } 314a4260ea4SJernej Skrabec 315a4260ea4SJernej Skrabec static bool deinterlace_check_format(u32 pixelformat) 316a4260ea4SJernej Skrabec { 317a4260ea4SJernej Skrabec unsigned int i; 318a4260ea4SJernej Skrabec 319a4260ea4SJernej Skrabec for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++) 320a4260ea4SJernej Skrabec if (deinterlace_formats[i] == pixelformat) 321a4260ea4SJernej Skrabec return true; 322a4260ea4SJernej Skrabec 323a4260ea4SJernej Skrabec return false; 324a4260ea4SJernej Skrabec } 325a4260ea4SJernej Skrabec 326a4260ea4SJernej Skrabec static void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt) 327a4260ea4SJernej Skrabec { 328a4260ea4SJernej Skrabec unsigned int height = pix_fmt->height; 329a4260ea4SJernej Skrabec unsigned int width = pix_fmt->width; 330a4260ea4SJernej Skrabec unsigned int bytesperline; 331a4260ea4SJernej Skrabec unsigned int sizeimage; 332a4260ea4SJernej Skrabec 333a4260ea4SJernej Skrabec width = clamp(width, DEINTERLACE_MIN_WIDTH, 334a4260ea4SJernej Skrabec DEINTERLACE_MAX_WIDTH); 335a4260ea4SJernej Skrabec height = clamp(height, DEINTERLACE_MIN_HEIGHT, 336a4260ea4SJernej Skrabec DEINTERLACE_MAX_HEIGHT); 337a4260ea4SJernej Skrabec 338a4260ea4SJernej Skrabec bytesperline = ALIGN(width, 2); 339a4260ea4SJernej Skrabec /* luma */ 340a4260ea4SJernej Skrabec sizeimage = bytesperline * height; 341a4260ea4SJernej Skrabec /* chroma */ 342a4260ea4SJernej Skrabec sizeimage += bytesperline * height / 2; 343a4260ea4SJernej Skrabec 344a4260ea4SJernej Skrabec pix_fmt->width = width; 345a4260ea4SJernej Skrabec pix_fmt->height = height; 346a4260ea4SJernej Skrabec pix_fmt->bytesperline = bytesperline; 347a4260ea4SJernej Skrabec pix_fmt->sizeimage = sizeimage; 348a4260ea4SJernej Skrabec } 349a4260ea4SJernej Skrabec 350a4260ea4SJernej Skrabec static int deinterlace_querycap(struct file *file, void *priv, 351a4260ea4SJernej Skrabec struct v4l2_capability *cap) 352a4260ea4SJernej Skrabec { 353a4260ea4SJernej Skrabec strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver)); 354a4260ea4SJernej Skrabec strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card)); 355a4260ea4SJernej Skrabec snprintf(cap->bus_info, sizeof(cap->bus_info), 356a4260ea4SJernej Skrabec "platform:%s", DEINTERLACE_NAME); 357a4260ea4SJernej Skrabec 358a4260ea4SJernej Skrabec return 0; 359a4260ea4SJernej Skrabec } 360a4260ea4SJernej Skrabec 361a4260ea4SJernej Skrabec static int deinterlace_enum_fmt(struct file *file, void *priv, 362a4260ea4SJernej Skrabec struct v4l2_fmtdesc *f) 363a4260ea4SJernej Skrabec { 364a4260ea4SJernej Skrabec if (f->index < ARRAY_SIZE(deinterlace_formats)) { 365a4260ea4SJernej Skrabec f->pixelformat = deinterlace_formats[f->index]; 366a4260ea4SJernej Skrabec 367a4260ea4SJernej Skrabec return 0; 368a4260ea4SJernej Skrabec } 369a4260ea4SJernej Skrabec 370a4260ea4SJernej Skrabec return -EINVAL; 371a4260ea4SJernej Skrabec } 372a4260ea4SJernej Skrabec 373a4260ea4SJernej Skrabec static int deinterlace_enum_framesizes(struct file *file, void *priv, 374a4260ea4SJernej Skrabec struct v4l2_frmsizeenum *fsize) 375a4260ea4SJernej Skrabec { 376a4260ea4SJernej Skrabec if (fsize->index != 0) 377a4260ea4SJernej Skrabec return -EINVAL; 378a4260ea4SJernej Skrabec 379a4260ea4SJernej Skrabec if (!deinterlace_check_format(fsize->pixel_format)) 380a4260ea4SJernej Skrabec return -EINVAL; 381a4260ea4SJernej Skrabec 382a4260ea4SJernej Skrabec fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 383a4260ea4SJernej Skrabec fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH; 384a4260ea4SJernej Skrabec fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT; 385a4260ea4SJernej Skrabec fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH; 386a4260ea4SJernej Skrabec fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT; 387a4260ea4SJernej Skrabec fsize->stepwise.step_width = 2; 388a4260ea4SJernej Skrabec fsize->stepwise.step_height = 1; 389a4260ea4SJernej Skrabec 390a4260ea4SJernej Skrabec return 0; 391a4260ea4SJernej Skrabec } 392a4260ea4SJernej Skrabec 393a4260ea4SJernej Skrabec static int deinterlace_g_fmt_vid_cap(struct file *file, void *priv, 394a4260ea4SJernej Skrabec struct v4l2_format *f) 395a4260ea4SJernej Skrabec { 396a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); 397a4260ea4SJernej Skrabec 398a4260ea4SJernej Skrabec f->fmt.pix = ctx->dst_fmt; 399a4260ea4SJernej Skrabec 400a4260ea4SJernej Skrabec return 0; 401a4260ea4SJernej Skrabec } 402a4260ea4SJernej Skrabec 403a4260ea4SJernej Skrabec static int deinterlace_g_fmt_vid_out(struct file *file, void *priv, 404a4260ea4SJernej Skrabec struct v4l2_format *f) 405a4260ea4SJernej Skrabec { 406a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); 407a4260ea4SJernej Skrabec 408a4260ea4SJernej Skrabec f->fmt.pix = ctx->src_fmt; 409a4260ea4SJernej Skrabec 410a4260ea4SJernej Skrabec return 0; 411a4260ea4SJernej Skrabec } 412a4260ea4SJernej Skrabec 413a4260ea4SJernej Skrabec static int deinterlace_try_fmt_vid_cap(struct file *file, void *priv, 414a4260ea4SJernej Skrabec struct v4l2_format *f) 415a4260ea4SJernej Skrabec { 416a4260ea4SJernej Skrabec if (!deinterlace_check_format(f->fmt.pix.pixelformat)) 417a4260ea4SJernej Skrabec f->fmt.pix.pixelformat = deinterlace_formats[0]; 418a4260ea4SJernej Skrabec 419a4260ea4SJernej Skrabec if (f->fmt.pix.field != V4L2_FIELD_NONE) 420a4260ea4SJernej Skrabec f->fmt.pix.field = V4L2_FIELD_NONE; 421a4260ea4SJernej Skrabec 422a4260ea4SJernej Skrabec deinterlace_prepare_format(&f->fmt.pix); 423a4260ea4SJernej Skrabec 424a4260ea4SJernej Skrabec return 0; 425a4260ea4SJernej Skrabec } 426a4260ea4SJernej Skrabec 427a4260ea4SJernej Skrabec static int deinterlace_try_fmt_vid_out(struct file *file, void *priv, 428a4260ea4SJernej Skrabec struct v4l2_format *f) 429a4260ea4SJernej Skrabec { 430a4260ea4SJernej Skrabec if (!deinterlace_check_format(f->fmt.pix.pixelformat)) 431a4260ea4SJernej Skrabec f->fmt.pix.pixelformat = deinterlace_formats[0]; 432a4260ea4SJernej Skrabec 433a4260ea4SJernej Skrabec if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB && 434a4260ea4SJernej Skrabec f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT && 435a4260ea4SJernej Skrabec f->fmt.pix.field != V4L2_FIELD_INTERLACED) 436a4260ea4SJernej Skrabec f->fmt.pix.field = V4L2_FIELD_INTERLACED; 437a4260ea4SJernej Skrabec 438a4260ea4SJernej Skrabec deinterlace_prepare_format(&f->fmt.pix); 439a4260ea4SJernej Skrabec 440a4260ea4SJernej Skrabec return 0; 441a4260ea4SJernej Skrabec } 442a4260ea4SJernej Skrabec 443a4260ea4SJernej Skrabec static int deinterlace_s_fmt_vid_cap(struct file *file, void *priv, 444a4260ea4SJernej Skrabec struct v4l2_format *f) 445a4260ea4SJernej Skrabec { 446a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); 447a4260ea4SJernej Skrabec struct vb2_queue *vq; 448a4260ea4SJernej Skrabec int ret; 449a4260ea4SJernej Skrabec 450a4260ea4SJernej Skrabec ret = deinterlace_try_fmt_vid_cap(file, priv, f); 451a4260ea4SJernej Skrabec if (ret) 452a4260ea4SJernej Skrabec return ret; 453a4260ea4SJernej Skrabec 454a4260ea4SJernej Skrabec vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 455a4260ea4SJernej Skrabec if (vb2_is_busy(vq)) 456a4260ea4SJernej Skrabec return -EBUSY; 457a4260ea4SJernej Skrabec 458a4260ea4SJernej Skrabec ctx->dst_fmt = f->fmt.pix; 459a4260ea4SJernej Skrabec 460a4260ea4SJernej Skrabec return 0; 461a4260ea4SJernej Skrabec } 462a4260ea4SJernej Skrabec 463a4260ea4SJernej Skrabec static int deinterlace_s_fmt_vid_out(struct file *file, void *priv, 464a4260ea4SJernej Skrabec struct v4l2_format *f) 465a4260ea4SJernej Skrabec { 466a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); 467a4260ea4SJernej Skrabec struct vb2_queue *vq; 468a4260ea4SJernej Skrabec int ret; 469a4260ea4SJernej Skrabec 470a4260ea4SJernej Skrabec ret = deinterlace_try_fmt_vid_out(file, priv, f); 471a4260ea4SJernej Skrabec if (ret) 472a4260ea4SJernej Skrabec return ret; 473a4260ea4SJernej Skrabec 474a4260ea4SJernej Skrabec vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 475a4260ea4SJernej Skrabec if (vb2_is_busy(vq)) 476a4260ea4SJernej Skrabec return -EBUSY; 477a4260ea4SJernej Skrabec 478a4260ea4SJernej Skrabec ctx->src_fmt = f->fmt.pix; 479a4260ea4SJernej Skrabec 480a4260ea4SJernej Skrabec /* Propagate colorspace information to capture. */ 481a4260ea4SJernej Skrabec ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; 482a4260ea4SJernej Skrabec ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; 483a4260ea4SJernej Skrabec ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; 484a4260ea4SJernej Skrabec ctx->dst_fmt.quantization = f->fmt.pix.quantization; 485a4260ea4SJernej Skrabec 486a4260ea4SJernej Skrabec return 0; 487a4260ea4SJernej Skrabec } 488a4260ea4SJernej Skrabec 489a4260ea4SJernej Skrabec static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { 490a4260ea4SJernej Skrabec .vidioc_querycap = deinterlace_querycap, 491a4260ea4SJernej Skrabec 492a4260ea4SJernej Skrabec .vidioc_enum_framesizes = deinterlace_enum_framesizes, 493a4260ea4SJernej Skrabec 494a4260ea4SJernej Skrabec .vidioc_enum_fmt_vid_cap = deinterlace_enum_fmt, 495a4260ea4SJernej Skrabec .vidioc_g_fmt_vid_cap = deinterlace_g_fmt_vid_cap, 496a4260ea4SJernej Skrabec .vidioc_try_fmt_vid_cap = deinterlace_try_fmt_vid_cap, 497a4260ea4SJernej Skrabec .vidioc_s_fmt_vid_cap = deinterlace_s_fmt_vid_cap, 498a4260ea4SJernej Skrabec 499a4260ea4SJernej Skrabec .vidioc_enum_fmt_vid_out = deinterlace_enum_fmt, 500a4260ea4SJernej Skrabec .vidioc_g_fmt_vid_out = deinterlace_g_fmt_vid_out, 501a4260ea4SJernej Skrabec .vidioc_try_fmt_vid_out = deinterlace_try_fmt_vid_out, 502a4260ea4SJernej Skrabec .vidioc_s_fmt_vid_out = deinterlace_s_fmt_vid_out, 503a4260ea4SJernej Skrabec 504a4260ea4SJernej Skrabec .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 505a4260ea4SJernej Skrabec .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 506a4260ea4SJernej Skrabec .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 507a4260ea4SJernej Skrabec .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 508a4260ea4SJernej Skrabec .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 509a4260ea4SJernej Skrabec .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 510a4260ea4SJernej Skrabec .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 511a4260ea4SJernej Skrabec 512a4260ea4SJernej Skrabec .vidioc_streamon = v4l2_m2m_ioctl_streamon, 513a4260ea4SJernej Skrabec .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 514a4260ea4SJernej Skrabec }; 515a4260ea4SJernej Skrabec 516a4260ea4SJernej Skrabec static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 517a4260ea4SJernej Skrabec unsigned int *nplanes, unsigned int sizes[], 518a4260ea4SJernej Skrabec struct device *alloc_devs[]) 519a4260ea4SJernej Skrabec { 520a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); 521a4260ea4SJernej Skrabec struct v4l2_pix_format *pix_fmt; 522a4260ea4SJernej Skrabec 523a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) 524a4260ea4SJernej Skrabec pix_fmt = &ctx->src_fmt; 525a4260ea4SJernej Skrabec else 526a4260ea4SJernej Skrabec pix_fmt = &ctx->dst_fmt; 527a4260ea4SJernej Skrabec 528a4260ea4SJernej Skrabec if (*nplanes) { 529a4260ea4SJernej Skrabec if (sizes[0] < pix_fmt->sizeimage) 530a4260ea4SJernej Skrabec return -EINVAL; 531a4260ea4SJernej Skrabec } else { 532a4260ea4SJernej Skrabec sizes[0] = pix_fmt->sizeimage; 533a4260ea4SJernej Skrabec *nplanes = 1; 534a4260ea4SJernej Skrabec } 535a4260ea4SJernej Skrabec 536a4260ea4SJernej Skrabec return 0; 537a4260ea4SJernej Skrabec } 538a4260ea4SJernej Skrabec 539a4260ea4SJernej Skrabec static int deinterlace_buf_prepare(struct vb2_buffer *vb) 540a4260ea4SJernej Skrabec { 541a4260ea4SJernej Skrabec struct vb2_queue *vq = vb->vb2_queue; 542a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); 543a4260ea4SJernej Skrabec struct v4l2_pix_format *pix_fmt; 544a4260ea4SJernej Skrabec 545a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) 546a4260ea4SJernej Skrabec pix_fmt = &ctx->src_fmt; 547a4260ea4SJernej Skrabec else 548a4260ea4SJernej Skrabec pix_fmt = &ctx->dst_fmt; 549a4260ea4SJernej Skrabec 550a4260ea4SJernej Skrabec if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) 551a4260ea4SJernej Skrabec return -EINVAL; 552a4260ea4SJernej Skrabec 553a4260ea4SJernej Skrabec vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); 554a4260ea4SJernej Skrabec 555a4260ea4SJernej Skrabec return 0; 556a4260ea4SJernej Skrabec } 557a4260ea4SJernej Skrabec 558a4260ea4SJernej Skrabec static void deinterlace_buf_queue(struct vb2_buffer *vb) 559a4260ea4SJernej Skrabec { 560a4260ea4SJernej Skrabec struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 561a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 562a4260ea4SJernej Skrabec 563a4260ea4SJernej Skrabec v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 564a4260ea4SJernej Skrabec } 565a4260ea4SJernej Skrabec 566a4260ea4SJernej Skrabec static void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state) 567a4260ea4SJernej Skrabec { 568a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); 569a4260ea4SJernej Skrabec struct vb2_v4l2_buffer *vbuf; 570a4260ea4SJernej Skrabec 571a4260ea4SJernej Skrabec do { 572a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) 573a4260ea4SJernej Skrabec vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 574a4260ea4SJernej Skrabec else 575a4260ea4SJernej Skrabec vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 576a4260ea4SJernej Skrabec 577a4260ea4SJernej Skrabec if (vbuf) 578a4260ea4SJernej Skrabec v4l2_m2m_buf_done(vbuf, state); 579a4260ea4SJernej Skrabec } while (vbuf); 580a4260ea4SJernej Skrabec 581a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev) 582a4260ea4SJernej Skrabec v4l2_m2m_buf_done(ctx->prev, state); 583a4260ea4SJernej Skrabec } 584a4260ea4SJernej Skrabec 585a4260ea4SJernej Skrabec static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count) 586a4260ea4SJernej Skrabec { 587a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); 588a4260ea4SJernej Skrabec struct device *dev = ctx->dev->dev; 589a4260ea4SJernej Skrabec int ret; 590a4260ea4SJernej Skrabec 591a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 592a4260ea4SJernej Skrabec ret = pm_runtime_get_sync(dev); 593a4260ea4SJernej Skrabec if (ret < 0) { 594a4260ea4SJernej Skrabec dev_err(dev, "Failed to enable module\n"); 595a4260ea4SJernej Skrabec 596a4260ea4SJernej Skrabec goto err_runtime_get; 597a4260ea4SJernej Skrabec } 598a4260ea4SJernej Skrabec 599a4260ea4SJernej Skrabec ctx->first_field = 600a4260ea4SJernej Skrabec ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT; 601a4260ea4SJernej Skrabec ctx->field = ctx->first_field; 602a4260ea4SJernej Skrabec 603a4260ea4SJernej Skrabec ctx->prev = NULL; 604a4260ea4SJernej Skrabec ctx->aborting = 0; 605a4260ea4SJernej Skrabec 606a4260ea4SJernej Skrabec ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE, 607a4260ea4SJernej Skrabec &ctx->flag1_buf_dma, 608a4260ea4SJernej Skrabec GFP_KERNEL); 609a4260ea4SJernej Skrabec if (!ctx->flag1_buf) { 610a4260ea4SJernej Skrabec ret = -ENOMEM; 611a4260ea4SJernej Skrabec 612a4260ea4SJernej Skrabec goto err_no_mem1; 613a4260ea4SJernej Skrabec } 614a4260ea4SJernej Skrabec 615a4260ea4SJernej Skrabec ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE, 616a4260ea4SJernej Skrabec &ctx->flag2_buf_dma, 617a4260ea4SJernej Skrabec GFP_KERNEL); 618a4260ea4SJernej Skrabec if (!ctx->flag2_buf) { 619a4260ea4SJernej Skrabec ret = -ENOMEM; 620a4260ea4SJernej Skrabec 621a4260ea4SJernej Skrabec goto err_no_mem2; 622a4260ea4SJernej Skrabec } 623a4260ea4SJernej Skrabec } 624a4260ea4SJernej Skrabec 625a4260ea4SJernej Skrabec return 0; 626a4260ea4SJernej Skrabec 627a4260ea4SJernej Skrabec err_no_mem2: 628a4260ea4SJernej Skrabec dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, 629a4260ea4SJernej Skrabec ctx->flag1_buf_dma); 630a4260ea4SJernej Skrabec err_no_mem1: 631a4260ea4SJernej Skrabec pm_runtime_put(dev); 632a4260ea4SJernej Skrabec err_runtime_get: 633a4260ea4SJernej Skrabec deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); 634a4260ea4SJernej Skrabec 635a4260ea4SJernej Skrabec return ret; 636a4260ea4SJernej Skrabec } 637a4260ea4SJernej Skrabec 638a4260ea4SJernej Skrabec static void deinterlace_stop_streaming(struct vb2_queue *vq) 639a4260ea4SJernej Skrabec { 640a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); 641a4260ea4SJernej Skrabec 642a4260ea4SJernej Skrabec if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 643a4260ea4SJernej Skrabec struct device *dev = ctx->dev->dev; 644a4260ea4SJernej Skrabec 645a4260ea4SJernej Skrabec dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, 646a4260ea4SJernej Skrabec ctx->flag1_buf_dma); 647a4260ea4SJernej Skrabec dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf, 648a4260ea4SJernej Skrabec ctx->flag2_buf_dma); 649a4260ea4SJernej Skrabec 650a4260ea4SJernej Skrabec pm_runtime_put(dev); 651a4260ea4SJernej Skrabec } 652a4260ea4SJernej Skrabec 653a4260ea4SJernej Skrabec deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR); 654a4260ea4SJernej Skrabec } 655a4260ea4SJernej Skrabec 656a4260ea4SJernej Skrabec static const struct vb2_ops deinterlace_qops = { 657a4260ea4SJernej Skrabec .queue_setup = deinterlace_queue_setup, 658a4260ea4SJernej Skrabec .buf_prepare = deinterlace_buf_prepare, 659a4260ea4SJernej Skrabec .buf_queue = deinterlace_buf_queue, 660a4260ea4SJernej Skrabec .start_streaming = deinterlace_start_streaming, 661a4260ea4SJernej Skrabec .stop_streaming = deinterlace_stop_streaming, 662a4260ea4SJernej Skrabec .wait_prepare = vb2_ops_wait_prepare, 663a4260ea4SJernej Skrabec .wait_finish = vb2_ops_wait_finish, 664a4260ea4SJernej Skrabec }; 665a4260ea4SJernej Skrabec 666a4260ea4SJernej Skrabec static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, 667a4260ea4SJernej Skrabec struct vb2_queue *dst_vq) 668a4260ea4SJernej Skrabec { 669a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = priv; 670a4260ea4SJernej Skrabec int ret; 671a4260ea4SJernej Skrabec 672a4260ea4SJernej Skrabec src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 673a4260ea4SJernej Skrabec src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 674a4260ea4SJernej Skrabec src_vq->drv_priv = ctx; 675a4260ea4SJernej Skrabec src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 676a4260ea4SJernej Skrabec src_vq->min_buffers_needed = 1; 677a4260ea4SJernej Skrabec src_vq->ops = &deinterlace_qops; 678a4260ea4SJernej Skrabec src_vq->mem_ops = &vb2_dma_contig_memops; 679a4260ea4SJernej Skrabec src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 680a4260ea4SJernej Skrabec src_vq->lock = &ctx->dev->dev_mutex; 681a4260ea4SJernej Skrabec src_vq->dev = ctx->dev->dev; 682a4260ea4SJernej Skrabec 683a4260ea4SJernej Skrabec ret = vb2_queue_init(src_vq); 684a4260ea4SJernej Skrabec if (ret) 685a4260ea4SJernej Skrabec return ret; 686a4260ea4SJernej Skrabec 687a4260ea4SJernej Skrabec dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 688a4260ea4SJernej Skrabec dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 689a4260ea4SJernej Skrabec dst_vq->drv_priv = ctx; 690a4260ea4SJernej Skrabec dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 691a4260ea4SJernej Skrabec dst_vq->min_buffers_needed = 2; 692a4260ea4SJernej Skrabec dst_vq->ops = &deinterlace_qops; 693a4260ea4SJernej Skrabec dst_vq->mem_ops = &vb2_dma_contig_memops; 694a4260ea4SJernej Skrabec dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 695a4260ea4SJernej Skrabec dst_vq->lock = &ctx->dev->dev_mutex; 696a4260ea4SJernej Skrabec dst_vq->dev = ctx->dev->dev; 697a4260ea4SJernej Skrabec 698a4260ea4SJernej Skrabec ret = vb2_queue_init(dst_vq); 699a4260ea4SJernej Skrabec if (ret) 700a4260ea4SJernej Skrabec return ret; 701a4260ea4SJernej Skrabec 702a4260ea4SJernej Skrabec return 0; 703a4260ea4SJernej Skrabec } 704a4260ea4SJernej Skrabec 705a4260ea4SJernej Skrabec static int deinterlace_open(struct file *file) 706a4260ea4SJernej Skrabec { 707a4260ea4SJernej Skrabec struct deinterlace_dev *dev = video_drvdata(file); 708a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = NULL; 709a4260ea4SJernej Skrabec int ret; 710a4260ea4SJernej Skrabec 711a4260ea4SJernej Skrabec if (mutex_lock_interruptible(&dev->dev_mutex)) 712a4260ea4SJernej Skrabec return -ERESTARTSYS; 713a4260ea4SJernej Skrabec 714a4260ea4SJernej Skrabec ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 715a4260ea4SJernej Skrabec if (!ctx) { 716a4260ea4SJernej Skrabec mutex_unlock(&dev->dev_mutex); 717a4260ea4SJernej Skrabec return -ENOMEM; 718a4260ea4SJernej Skrabec } 719a4260ea4SJernej Skrabec 720a4260ea4SJernej Skrabec /* default output format */ 721a4260ea4SJernej Skrabec ctx->src_fmt.pixelformat = deinterlace_formats[0]; 722a4260ea4SJernej Skrabec ctx->src_fmt.field = V4L2_FIELD_INTERLACED; 723a4260ea4SJernej Skrabec ctx->src_fmt.width = 640; 724a4260ea4SJernej Skrabec ctx->src_fmt.height = 480; 725a4260ea4SJernej Skrabec deinterlace_prepare_format(&ctx->src_fmt); 726a4260ea4SJernej Skrabec 727a4260ea4SJernej Skrabec /* default capture format */ 728a4260ea4SJernej Skrabec ctx->dst_fmt.pixelformat = deinterlace_formats[0]; 729a4260ea4SJernej Skrabec ctx->dst_fmt.field = V4L2_FIELD_NONE; 730a4260ea4SJernej Skrabec ctx->dst_fmt.width = 640; 731a4260ea4SJernej Skrabec ctx->dst_fmt.height = 480; 732a4260ea4SJernej Skrabec deinterlace_prepare_format(&ctx->dst_fmt); 733a4260ea4SJernej Skrabec 734a4260ea4SJernej Skrabec v4l2_fh_init(&ctx->fh, video_devdata(file)); 735a4260ea4SJernej Skrabec file->private_data = &ctx->fh; 736a4260ea4SJernej Skrabec ctx->dev = dev; 737a4260ea4SJernej Skrabec 738a4260ea4SJernej Skrabec ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 739a4260ea4SJernej Skrabec &deinterlace_queue_init); 740a4260ea4SJernej Skrabec if (IS_ERR(ctx->fh.m2m_ctx)) { 741a4260ea4SJernej Skrabec ret = PTR_ERR(ctx->fh.m2m_ctx); 742a4260ea4SJernej Skrabec goto err_free; 743a4260ea4SJernej Skrabec } 744a4260ea4SJernej Skrabec 745a4260ea4SJernej Skrabec v4l2_fh_add(&ctx->fh); 746a4260ea4SJernej Skrabec 747a4260ea4SJernej Skrabec mutex_unlock(&dev->dev_mutex); 748a4260ea4SJernej Skrabec 749a4260ea4SJernej Skrabec return 0; 750a4260ea4SJernej Skrabec 751a4260ea4SJernej Skrabec err_free: 752a4260ea4SJernej Skrabec kfree(ctx); 753a4260ea4SJernej Skrabec mutex_unlock(&dev->dev_mutex); 754a4260ea4SJernej Skrabec 755a4260ea4SJernej Skrabec return ret; 756a4260ea4SJernej Skrabec } 757a4260ea4SJernej Skrabec 758a4260ea4SJernej Skrabec static int deinterlace_release(struct file *file) 759a4260ea4SJernej Skrabec { 760a4260ea4SJernej Skrabec struct deinterlace_dev *dev = video_drvdata(file); 761a4260ea4SJernej Skrabec struct deinterlace_ctx *ctx = container_of(file->private_data, 762a4260ea4SJernej Skrabec struct deinterlace_ctx, fh); 763a4260ea4SJernej Skrabec 764a4260ea4SJernej Skrabec mutex_lock(&dev->dev_mutex); 765a4260ea4SJernej Skrabec 766a4260ea4SJernej Skrabec v4l2_fh_del(&ctx->fh); 767a4260ea4SJernej Skrabec v4l2_fh_exit(&ctx->fh); 768a4260ea4SJernej Skrabec v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 769a4260ea4SJernej Skrabec 770a4260ea4SJernej Skrabec kfree(ctx); 771a4260ea4SJernej Skrabec 772a4260ea4SJernej Skrabec mutex_unlock(&dev->dev_mutex); 773a4260ea4SJernej Skrabec 774a4260ea4SJernej Skrabec return 0; 775a4260ea4SJernej Skrabec } 776a4260ea4SJernej Skrabec 777a4260ea4SJernej Skrabec static const struct v4l2_file_operations deinterlace_fops = { 778a4260ea4SJernej Skrabec .owner = THIS_MODULE, 779a4260ea4SJernej Skrabec .open = deinterlace_open, 780a4260ea4SJernej Skrabec .release = deinterlace_release, 781a4260ea4SJernej Skrabec .poll = v4l2_m2m_fop_poll, 782a4260ea4SJernej Skrabec .unlocked_ioctl = video_ioctl2, 783a4260ea4SJernej Skrabec .mmap = v4l2_m2m_fop_mmap, 784a4260ea4SJernej Skrabec }; 785a4260ea4SJernej Skrabec 786a4260ea4SJernej Skrabec static const struct video_device deinterlace_video_device = { 787a4260ea4SJernej Skrabec .name = DEINTERLACE_NAME, 788a4260ea4SJernej Skrabec .vfl_dir = VFL_DIR_M2M, 789a4260ea4SJernej Skrabec .fops = &deinterlace_fops, 790a4260ea4SJernej Skrabec .ioctl_ops = &deinterlace_ioctl_ops, 791a4260ea4SJernej Skrabec .minor = -1, 792a4260ea4SJernej Skrabec .release = video_device_release_empty, 793a4260ea4SJernej Skrabec .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 794a4260ea4SJernej Skrabec }; 795a4260ea4SJernej Skrabec 796a4260ea4SJernej Skrabec static const struct v4l2_m2m_ops deinterlace_m2m_ops = { 797a4260ea4SJernej Skrabec .device_run = deinterlace_device_run, 798a4260ea4SJernej Skrabec .job_ready = deinterlace_job_ready, 799a4260ea4SJernej Skrabec .job_abort = deinterlace_job_abort, 800a4260ea4SJernej Skrabec }; 801a4260ea4SJernej Skrabec 802a4260ea4SJernej Skrabec static int deinterlace_probe(struct platform_device *pdev) 803a4260ea4SJernej Skrabec { 804a4260ea4SJernej Skrabec struct deinterlace_dev *dev; 805a4260ea4SJernej Skrabec struct video_device *vfd; 806a4260ea4SJernej Skrabec struct resource *res; 807a4260ea4SJernej Skrabec int irq, ret; 808a4260ea4SJernej Skrabec 809a4260ea4SJernej Skrabec dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 810a4260ea4SJernej Skrabec if (!dev) 811a4260ea4SJernej Skrabec return -ENOMEM; 812a4260ea4SJernej Skrabec 813a4260ea4SJernej Skrabec dev->vfd = deinterlace_video_device; 814a4260ea4SJernej Skrabec dev->dev = &pdev->dev; 815a4260ea4SJernej Skrabec 816a4260ea4SJernej Skrabec irq = platform_get_irq(pdev, 0); 817a4260ea4SJernej Skrabec if (irq <= 0) { 818a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to get IRQ\n"); 819a4260ea4SJernej Skrabec 820a4260ea4SJernej Skrabec return irq; 821a4260ea4SJernej Skrabec } 822a4260ea4SJernej Skrabec 823a4260ea4SJernej Skrabec ret = devm_request_irq(dev->dev, irq, deinterlace_irq, 824a4260ea4SJernej Skrabec 0, dev_name(dev->dev), dev); 825a4260ea4SJernej Skrabec if (ret) { 826a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to request IRQ\n"); 827a4260ea4SJernej Skrabec 828a4260ea4SJernej Skrabec return ret; 829a4260ea4SJernej Skrabec } 830a4260ea4SJernej Skrabec 831a4260ea4SJernej Skrabec ret = of_dma_configure(dev->dev, dev->dev->of_node, true); 832a4260ea4SJernej Skrabec if (ret) 833a4260ea4SJernej Skrabec return ret; 834a4260ea4SJernej Skrabec 835a4260ea4SJernej Skrabec res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 836a4260ea4SJernej Skrabec dev->base = devm_ioremap_resource(&pdev->dev, res); 8370d962c3aSWei Yongjun if (IS_ERR(dev->base)) 838a4260ea4SJernej Skrabec return PTR_ERR(dev->base); 839a4260ea4SJernej Skrabec 840a4260ea4SJernej Skrabec dev->bus_clk = devm_clk_get(dev->dev, "bus"); 841a4260ea4SJernej Skrabec if (IS_ERR(dev->bus_clk)) { 842a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to get bus clock\n"); 843a4260ea4SJernej Skrabec 844a4260ea4SJernej Skrabec return PTR_ERR(dev->bus_clk); 845a4260ea4SJernej Skrabec } 846a4260ea4SJernej Skrabec 847a4260ea4SJernej Skrabec dev->mod_clk = devm_clk_get(dev->dev, "mod"); 848a4260ea4SJernej Skrabec if (IS_ERR(dev->mod_clk)) { 849a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to get mod clock\n"); 850a4260ea4SJernej Skrabec 851a4260ea4SJernej Skrabec return PTR_ERR(dev->mod_clk); 852a4260ea4SJernej Skrabec } 853a4260ea4SJernej Skrabec 854a4260ea4SJernej Skrabec dev->ram_clk = devm_clk_get(dev->dev, "ram"); 855a4260ea4SJernej Skrabec if (IS_ERR(dev->ram_clk)) { 856a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to get ram clock\n"); 857a4260ea4SJernej Skrabec 858a4260ea4SJernej Skrabec return PTR_ERR(dev->ram_clk); 859a4260ea4SJernej Skrabec } 860a4260ea4SJernej Skrabec 861a4260ea4SJernej Skrabec dev->rstc = devm_reset_control_get(dev->dev, NULL); 862a4260ea4SJernej Skrabec if (IS_ERR(dev->rstc)) { 863a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to get reset control\n"); 864a4260ea4SJernej Skrabec 865a4260ea4SJernej Skrabec return PTR_ERR(dev->rstc); 866a4260ea4SJernej Skrabec } 867a4260ea4SJernej Skrabec 868a4260ea4SJernej Skrabec mutex_init(&dev->dev_mutex); 869a4260ea4SJernej Skrabec 870a4260ea4SJernej Skrabec ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 871a4260ea4SJernej Skrabec if (ret) { 872a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to register V4L2 device\n"); 873a4260ea4SJernej Skrabec 874a4260ea4SJernej Skrabec return ret; 875a4260ea4SJernej Skrabec } 876a4260ea4SJernej Skrabec 877a4260ea4SJernej Skrabec vfd = &dev->vfd; 878a4260ea4SJernej Skrabec vfd->lock = &dev->dev_mutex; 879a4260ea4SJernej Skrabec vfd->v4l2_dev = &dev->v4l2_dev; 880a4260ea4SJernej Skrabec 881a4260ea4SJernej Skrabec snprintf(vfd->name, sizeof(vfd->name), "%s", 882a4260ea4SJernej Skrabec deinterlace_video_device.name); 883a4260ea4SJernej Skrabec video_set_drvdata(vfd, dev); 884a4260ea4SJernej Skrabec 88570cad449SHans Verkuil ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 886a4260ea4SJernej Skrabec if (ret) { 887a4260ea4SJernej Skrabec v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 888a4260ea4SJernej Skrabec 889a4260ea4SJernej Skrabec goto err_v4l2; 890a4260ea4SJernej Skrabec } 891a4260ea4SJernej Skrabec 892a4260ea4SJernej Skrabec v4l2_info(&dev->v4l2_dev, 893a4260ea4SJernej Skrabec "Device registered as /dev/video%d\n", vfd->num); 894a4260ea4SJernej Skrabec 895a4260ea4SJernej Skrabec dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops); 896a4260ea4SJernej Skrabec if (IS_ERR(dev->m2m_dev)) { 897a4260ea4SJernej Skrabec v4l2_err(&dev->v4l2_dev, 898a4260ea4SJernej Skrabec "Failed to initialize V4L2 M2M device\n"); 899a4260ea4SJernej Skrabec ret = PTR_ERR(dev->m2m_dev); 900a4260ea4SJernej Skrabec 901a4260ea4SJernej Skrabec goto err_video; 902a4260ea4SJernej Skrabec } 903a4260ea4SJernej Skrabec 904a4260ea4SJernej Skrabec platform_set_drvdata(pdev, dev); 905a4260ea4SJernej Skrabec 906a4260ea4SJernej Skrabec pm_runtime_enable(dev->dev); 907a4260ea4SJernej Skrabec 908a4260ea4SJernej Skrabec return 0; 909a4260ea4SJernej Skrabec 910a4260ea4SJernej Skrabec err_video: 911a4260ea4SJernej Skrabec video_unregister_device(&dev->vfd); 912a4260ea4SJernej Skrabec err_v4l2: 913a4260ea4SJernej Skrabec v4l2_device_unregister(&dev->v4l2_dev); 914a4260ea4SJernej Skrabec 915a4260ea4SJernej Skrabec return ret; 916a4260ea4SJernej Skrabec } 917a4260ea4SJernej Skrabec 918a4260ea4SJernej Skrabec static int deinterlace_remove(struct platform_device *pdev) 919a4260ea4SJernej Skrabec { 920a4260ea4SJernej Skrabec struct deinterlace_dev *dev = platform_get_drvdata(pdev); 921a4260ea4SJernej Skrabec 922a4260ea4SJernej Skrabec v4l2_m2m_release(dev->m2m_dev); 923a4260ea4SJernej Skrabec video_unregister_device(&dev->vfd); 924a4260ea4SJernej Skrabec v4l2_device_unregister(&dev->v4l2_dev); 925a4260ea4SJernej Skrabec 926a4260ea4SJernej Skrabec pm_runtime_force_suspend(&pdev->dev); 927a4260ea4SJernej Skrabec 928a4260ea4SJernej Skrabec return 0; 929a4260ea4SJernej Skrabec } 930a4260ea4SJernej Skrabec 931a4260ea4SJernej Skrabec static int deinterlace_runtime_resume(struct device *device) 932a4260ea4SJernej Skrabec { 933a4260ea4SJernej Skrabec struct deinterlace_dev *dev = dev_get_drvdata(device); 934a4260ea4SJernej Skrabec int ret; 935a4260ea4SJernej Skrabec 936a4260ea4SJernej Skrabec ret = clk_set_rate_exclusive(dev->mod_clk, 300000000); 937a4260ea4SJernej Skrabec if (ret) { 938a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to set exclusive mod clock rate\n"); 939a4260ea4SJernej Skrabec 940a4260ea4SJernej Skrabec return ret; 941a4260ea4SJernej Skrabec } 942a4260ea4SJernej Skrabec 943a4260ea4SJernej Skrabec ret = clk_prepare_enable(dev->bus_clk); 944a4260ea4SJernej Skrabec if (ret) { 945a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to enable bus clock\n"); 946a4260ea4SJernej Skrabec 947a4260ea4SJernej Skrabec goto err_exlusive_rate; 948a4260ea4SJernej Skrabec } 949a4260ea4SJernej Skrabec 950a4260ea4SJernej Skrabec ret = clk_prepare_enable(dev->mod_clk); 951a4260ea4SJernej Skrabec if (ret) { 952a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to enable mod clock\n"); 953a4260ea4SJernej Skrabec 954a4260ea4SJernej Skrabec goto err_bus_clk; 955a4260ea4SJernej Skrabec } 956a4260ea4SJernej Skrabec 957a4260ea4SJernej Skrabec ret = clk_prepare_enable(dev->ram_clk); 958a4260ea4SJernej Skrabec if (ret) { 959a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to enable ram clock\n"); 960a4260ea4SJernej Skrabec 961a4260ea4SJernej Skrabec goto err_mod_clk; 962a4260ea4SJernej Skrabec } 963a4260ea4SJernej Skrabec 964a4260ea4SJernej Skrabec ret = reset_control_deassert(dev->rstc); 965a4260ea4SJernej Skrabec if (ret) { 966a4260ea4SJernej Skrabec dev_err(dev->dev, "Failed to apply reset\n"); 967a4260ea4SJernej Skrabec 968a4260ea4SJernej Skrabec goto err_ram_clk; 969a4260ea4SJernej Skrabec } 970a4260ea4SJernej Skrabec 971a4260ea4SJernej Skrabec deinterlace_init(dev); 972a4260ea4SJernej Skrabec 973a4260ea4SJernej Skrabec return 0; 974a4260ea4SJernej Skrabec 975a4260ea4SJernej Skrabec err_exlusive_rate: 976a4260ea4SJernej Skrabec clk_rate_exclusive_put(dev->mod_clk); 977a4260ea4SJernej Skrabec err_ram_clk: 978a4260ea4SJernej Skrabec clk_disable_unprepare(dev->ram_clk); 979a4260ea4SJernej Skrabec err_mod_clk: 980a4260ea4SJernej Skrabec clk_disable_unprepare(dev->mod_clk); 981a4260ea4SJernej Skrabec err_bus_clk: 982a4260ea4SJernej Skrabec clk_disable_unprepare(dev->bus_clk); 983a4260ea4SJernej Skrabec 984a4260ea4SJernej Skrabec return ret; 985a4260ea4SJernej Skrabec } 986a4260ea4SJernej Skrabec 987a4260ea4SJernej Skrabec static int deinterlace_runtime_suspend(struct device *device) 988a4260ea4SJernej Skrabec { 989a4260ea4SJernej Skrabec struct deinterlace_dev *dev = dev_get_drvdata(device); 990a4260ea4SJernej Skrabec 991a4260ea4SJernej Skrabec reset_control_assert(dev->rstc); 992a4260ea4SJernej Skrabec 993a4260ea4SJernej Skrabec clk_disable_unprepare(dev->ram_clk); 994a4260ea4SJernej Skrabec clk_disable_unprepare(dev->mod_clk); 995a4260ea4SJernej Skrabec clk_disable_unprepare(dev->bus_clk); 996a4260ea4SJernej Skrabec clk_rate_exclusive_put(dev->mod_clk); 997a4260ea4SJernej Skrabec 998a4260ea4SJernej Skrabec return 0; 999a4260ea4SJernej Skrabec } 1000a4260ea4SJernej Skrabec 1001a4260ea4SJernej Skrabec static const struct of_device_id deinterlace_dt_match[] = { 1002a4260ea4SJernej Skrabec { .compatible = "allwinner,sun8i-h3-deinterlace" }, 1003a4260ea4SJernej Skrabec { /* sentinel */ } 1004a4260ea4SJernej Skrabec }; 1005a4260ea4SJernej Skrabec MODULE_DEVICE_TABLE(of, deinterlace_dt_match); 1006a4260ea4SJernej Skrabec 1007a4260ea4SJernej Skrabec static const struct dev_pm_ops deinterlace_pm_ops = { 1008a4260ea4SJernej Skrabec .runtime_resume = deinterlace_runtime_resume, 1009a4260ea4SJernej Skrabec .runtime_suspend = deinterlace_runtime_suspend, 1010a4260ea4SJernej Skrabec }; 1011a4260ea4SJernej Skrabec 1012a4260ea4SJernej Skrabec static struct platform_driver deinterlace_driver = { 1013a4260ea4SJernej Skrabec .probe = deinterlace_probe, 1014a4260ea4SJernej Skrabec .remove = deinterlace_remove, 1015a4260ea4SJernej Skrabec .driver = { 1016a4260ea4SJernej Skrabec .name = DEINTERLACE_NAME, 1017a4260ea4SJernej Skrabec .of_match_table = deinterlace_dt_match, 1018a4260ea4SJernej Skrabec .pm = &deinterlace_pm_ops, 1019a4260ea4SJernej Skrabec }, 1020a4260ea4SJernej Skrabec }; 1021a4260ea4SJernej Skrabec module_platform_driver(deinterlace_driver); 1022a4260ea4SJernej Skrabec 1023a4260ea4SJernej Skrabec MODULE_LICENSE("GPL v2"); 1024a4260ea4SJernej Skrabec MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>"); 1025a4260ea4SJernej Skrabec MODULE_DESCRIPTION("Allwinner Deinterlace driver"); 1026