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