162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013 Texas Instruments Inc. 662306a36Sopenharmony_ci * David Griego, <dagriego@biglakesoftware.com> 762306a36Sopenharmony_ci * Dale Farnsworth, <dale@farnsworth.org> 862306a36Sopenharmony_ci * Archit Taneja, <archit@ti.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. 1162306a36Sopenharmony_ci * Pawel Osciak, <pawel@osciak.com> 1262306a36Sopenharmony_ci * Marek Szyprowski, <m.szyprowski@samsung.com> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Based on the virtual v4l2-mem2mem example device 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/ioctl.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2862306a36Sopenharmony_ci#include <linux/sched.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/videodev2.h> 3162306a36Sopenharmony_ci#include <linux/log2.h> 3262306a36Sopenharmony_ci#include <linux/sizes.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <media/v4l2-common.h> 3562306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3662306a36Sopenharmony_ci#include <media/v4l2-device.h> 3762306a36Sopenharmony_ci#include <media/v4l2-event.h> 3862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3962306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 4062306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 4162306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "vpdma.h" 4462306a36Sopenharmony_ci#include "vpdma_priv.h" 4562306a36Sopenharmony_ci#include "vpe_regs.h" 4662306a36Sopenharmony_ci#include "sc.h" 4762306a36Sopenharmony_ci#include "csc.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define VPE_MODULE_NAME "vpe" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* minimum and maximum frame sizes */ 5262306a36Sopenharmony_ci#define MIN_W 32 5362306a36Sopenharmony_ci#define MIN_H 32 5462306a36Sopenharmony_ci#define MAX_W 2048 5562306a36Sopenharmony_ci#define MAX_H 2048 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* required alignments */ 5862306a36Sopenharmony_ci#define S_ALIGN 0 /* multiple of 1 */ 5962306a36Sopenharmony_ci#define H_ALIGN 1 /* multiple of 2 */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* flags that indicate a format can be used for capture/output */ 6262306a36Sopenharmony_ci#define VPE_FMT_TYPE_CAPTURE (1 << 0) 6362306a36Sopenharmony_ci#define VPE_FMT_TYPE_OUTPUT (1 << 1) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* used as plane indices */ 6662306a36Sopenharmony_ci#define VPE_MAX_PLANES 2 6762306a36Sopenharmony_ci#define VPE_LUMA 0 6862306a36Sopenharmony_ci#define VPE_CHROMA 1 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* per m2m context info */ 7162306a36Sopenharmony_ci#define VPE_MAX_SRC_BUFS 3 /* need 3 src fields to de-interlace */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * each VPE context can need up to 3 config descriptors, 7 input descriptors, 7762306a36Sopenharmony_ci * 3 output descriptors, and 10 control descriptors 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci#define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \ 8062306a36Sopenharmony_ci 13 * VPDMA_CFD_CTD_DESC_SIZE) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define vpe_dbg(vpedev, fmt, arg...) \ 8362306a36Sopenharmony_ci dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg) 8462306a36Sopenharmony_ci#define vpe_err(vpedev, fmt, arg...) \ 8562306a36Sopenharmony_ci dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct vpe_us_coeffs { 8862306a36Sopenharmony_ci unsigned short anchor_fid0_c0; 8962306a36Sopenharmony_ci unsigned short anchor_fid0_c1; 9062306a36Sopenharmony_ci unsigned short anchor_fid0_c2; 9162306a36Sopenharmony_ci unsigned short anchor_fid0_c3; 9262306a36Sopenharmony_ci unsigned short interp_fid0_c0; 9362306a36Sopenharmony_ci unsigned short interp_fid0_c1; 9462306a36Sopenharmony_ci unsigned short interp_fid0_c2; 9562306a36Sopenharmony_ci unsigned short interp_fid0_c3; 9662306a36Sopenharmony_ci unsigned short anchor_fid1_c0; 9762306a36Sopenharmony_ci unsigned short anchor_fid1_c1; 9862306a36Sopenharmony_ci unsigned short anchor_fid1_c2; 9962306a36Sopenharmony_ci unsigned short anchor_fid1_c3; 10062306a36Sopenharmony_ci unsigned short interp_fid1_c0; 10162306a36Sopenharmony_ci unsigned short interp_fid1_c1; 10262306a36Sopenharmony_ci unsigned short interp_fid1_c2; 10362306a36Sopenharmony_ci unsigned short interp_fid1_c3; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Default upsampler coefficients 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic const struct vpe_us_coeffs us_coeffs[] = { 11062306a36Sopenharmony_ci { 11162306a36Sopenharmony_ci /* Coefficients for progressive input */ 11262306a36Sopenharmony_ci 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, 11362306a36Sopenharmony_ci 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, 11462306a36Sopenharmony_ci }, 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci /* Coefficients for Top Field Interlaced input */ 11762306a36Sopenharmony_ci 0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3, 11862306a36Sopenharmony_ci /* Coefficients for Bottom Field Interlaced input */ 11962306a36Sopenharmony_ci 0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9, 12062306a36Sopenharmony_ci }, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * the following registers are for configuring some of the parameters of the 12562306a36Sopenharmony_ci * motion and edge detection blocks inside DEI, these generally remain the same, 12662306a36Sopenharmony_ci * these could be passed later via userspace if some one needs to tweak these. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistruct vpe_dei_regs { 12962306a36Sopenharmony_ci unsigned long mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */ 13062306a36Sopenharmony_ci unsigned long edi_config_reg; /* VPE_DEI_REG3 */ 13162306a36Sopenharmony_ci unsigned long edi_lut_reg0; /* VPE_DEI_REG4 */ 13262306a36Sopenharmony_ci unsigned long edi_lut_reg1; /* VPE_DEI_REG5 */ 13362306a36Sopenharmony_ci unsigned long edi_lut_reg2; /* VPE_DEI_REG6 */ 13462306a36Sopenharmony_ci unsigned long edi_lut_reg3; /* VPE_DEI_REG7 */ 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * default expert DEI register values, unlikely to be modified. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic const struct vpe_dei_regs dei_regs = { 14162306a36Sopenharmony_ci .mdt_spacial_freq_thr_reg = 0x020C0804u, 14262306a36Sopenharmony_ci .edi_config_reg = 0x0118100Cu, 14362306a36Sopenharmony_ci .edi_lut_reg0 = 0x08040200u, 14462306a36Sopenharmony_ci .edi_lut_reg1 = 0x1010100Cu, 14562306a36Sopenharmony_ci .edi_lut_reg2 = 0x10101010u, 14662306a36Sopenharmony_ci .edi_lut_reg3 = 0x10101010u, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * The port_data structure contains per-port data. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistruct vpe_port_data { 15362306a36Sopenharmony_ci enum vpdma_channel channel; /* VPDMA channel */ 15462306a36Sopenharmony_ci u8 vb_index; /* input frame f, f-1, f-2 index */ 15562306a36Sopenharmony_ci u8 vb_part; /* plane index for co-panar formats */ 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Define indices into the port_data tables 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci#define VPE_PORT_LUMA1_IN 0 16262306a36Sopenharmony_ci#define VPE_PORT_CHROMA1_IN 1 16362306a36Sopenharmony_ci#define VPE_PORT_LUMA2_IN 2 16462306a36Sopenharmony_ci#define VPE_PORT_CHROMA2_IN 3 16562306a36Sopenharmony_ci#define VPE_PORT_LUMA3_IN 4 16662306a36Sopenharmony_ci#define VPE_PORT_CHROMA3_IN 5 16762306a36Sopenharmony_ci#define VPE_PORT_MV_IN 6 16862306a36Sopenharmony_ci#define VPE_PORT_MV_OUT 7 16962306a36Sopenharmony_ci#define VPE_PORT_LUMA_OUT 8 17062306a36Sopenharmony_ci#define VPE_PORT_CHROMA_OUT 9 17162306a36Sopenharmony_ci#define VPE_PORT_RGB_OUT 10 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct vpe_port_data port_data[11] = { 17462306a36Sopenharmony_ci [VPE_PORT_LUMA1_IN] = { 17562306a36Sopenharmony_ci .channel = VPE_CHAN_LUMA1_IN, 17662306a36Sopenharmony_ci .vb_index = 0, 17762306a36Sopenharmony_ci .vb_part = VPE_LUMA, 17862306a36Sopenharmony_ci }, 17962306a36Sopenharmony_ci [VPE_PORT_CHROMA1_IN] = { 18062306a36Sopenharmony_ci .channel = VPE_CHAN_CHROMA1_IN, 18162306a36Sopenharmony_ci .vb_index = 0, 18262306a36Sopenharmony_ci .vb_part = VPE_CHROMA, 18362306a36Sopenharmony_ci }, 18462306a36Sopenharmony_ci [VPE_PORT_LUMA2_IN] = { 18562306a36Sopenharmony_ci .channel = VPE_CHAN_LUMA2_IN, 18662306a36Sopenharmony_ci .vb_index = 1, 18762306a36Sopenharmony_ci .vb_part = VPE_LUMA, 18862306a36Sopenharmony_ci }, 18962306a36Sopenharmony_ci [VPE_PORT_CHROMA2_IN] = { 19062306a36Sopenharmony_ci .channel = VPE_CHAN_CHROMA2_IN, 19162306a36Sopenharmony_ci .vb_index = 1, 19262306a36Sopenharmony_ci .vb_part = VPE_CHROMA, 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci [VPE_PORT_LUMA3_IN] = { 19562306a36Sopenharmony_ci .channel = VPE_CHAN_LUMA3_IN, 19662306a36Sopenharmony_ci .vb_index = 2, 19762306a36Sopenharmony_ci .vb_part = VPE_LUMA, 19862306a36Sopenharmony_ci }, 19962306a36Sopenharmony_ci [VPE_PORT_CHROMA3_IN] = { 20062306a36Sopenharmony_ci .channel = VPE_CHAN_CHROMA3_IN, 20162306a36Sopenharmony_ci .vb_index = 2, 20262306a36Sopenharmony_ci .vb_part = VPE_CHROMA, 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci [VPE_PORT_MV_IN] = { 20562306a36Sopenharmony_ci .channel = VPE_CHAN_MV_IN, 20662306a36Sopenharmony_ci }, 20762306a36Sopenharmony_ci [VPE_PORT_MV_OUT] = { 20862306a36Sopenharmony_ci .channel = VPE_CHAN_MV_OUT, 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci [VPE_PORT_LUMA_OUT] = { 21162306a36Sopenharmony_ci .channel = VPE_CHAN_LUMA_OUT, 21262306a36Sopenharmony_ci .vb_part = VPE_LUMA, 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci [VPE_PORT_CHROMA_OUT] = { 21562306a36Sopenharmony_ci .channel = VPE_CHAN_CHROMA_OUT, 21662306a36Sopenharmony_ci .vb_part = VPE_CHROMA, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci [VPE_PORT_RGB_OUT] = { 21962306a36Sopenharmony_ci .channel = VPE_CHAN_RGB_OUT, 22062306a36Sopenharmony_ci .vb_part = VPE_LUMA, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* driver info for each of the supported video formats */ 22662306a36Sopenharmony_cistruct vpe_fmt { 22762306a36Sopenharmony_ci u32 fourcc; /* standard format identifier */ 22862306a36Sopenharmony_ci u8 types; /* CAPTURE and/or OUTPUT */ 22962306a36Sopenharmony_ci u8 coplanar; /* set for unpacked Luma and Chroma */ 23062306a36Sopenharmony_ci /* vpdma format info for each plane */ 23162306a36Sopenharmony_ci struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES]; 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct vpe_fmt vpe_formats[] = { 23562306a36Sopenharmony_ci { 23662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV16, 23762306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, 23862306a36Sopenharmony_ci .coplanar = 1, 23962306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444], 24062306a36Sopenharmony_ci &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444], 24162306a36Sopenharmony_ci }, 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci { 24462306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV12, 24562306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, 24662306a36Sopenharmony_ci .coplanar = 1, 24762306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], 24862306a36Sopenharmony_ci &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], 24962306a36Sopenharmony_ci }, 25062306a36Sopenharmony_ci }, 25162306a36Sopenharmony_ci { 25262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV21, 25362306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, 25462306a36Sopenharmony_ci .coplanar = 1, 25562306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], 25662306a36Sopenharmony_ci &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420], 25762306a36Sopenharmony_ci }, 25862306a36Sopenharmony_ci }, 25962306a36Sopenharmony_ci { 26062306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 26162306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, 26262306a36Sopenharmony_ci .coplanar = 0, 26362306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci }, 26662306a36Sopenharmony_ci { 26762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 26862306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, 26962306a36Sopenharmony_ci .coplanar = 0, 27062306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], 27162306a36Sopenharmony_ci }, 27262306a36Sopenharmony_ci }, 27362306a36Sopenharmony_ci { 27462306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB24, 27562306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 27662306a36Sopenharmony_ci .coplanar = 0, 27762306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], 27862306a36Sopenharmony_ci }, 27962306a36Sopenharmony_ci }, 28062306a36Sopenharmony_ci { 28162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB32, 28262306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 28362306a36Sopenharmony_ci .coplanar = 0, 28462306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], 28562306a36Sopenharmony_ci }, 28662306a36Sopenharmony_ci }, 28762306a36Sopenharmony_ci { 28862306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR24, 28962306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 29062306a36Sopenharmony_ci .coplanar = 0, 29162306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci }, 29462306a36Sopenharmony_ci { 29562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR32, 29662306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 29762306a36Sopenharmony_ci .coplanar = 0, 29862306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], 29962306a36Sopenharmony_ci }, 30062306a36Sopenharmony_ci }, 30162306a36Sopenharmony_ci { 30262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 30362306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 30462306a36Sopenharmony_ci .coplanar = 0, 30562306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB565], 30662306a36Sopenharmony_ci }, 30762306a36Sopenharmony_ci }, 30862306a36Sopenharmony_ci { 30962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB555, 31062306a36Sopenharmony_ci .types = VPE_FMT_TYPE_CAPTURE, 31162306a36Sopenharmony_ci .coplanar = 0, 31262306a36Sopenharmony_ci .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGBA16_5551], 31362306a36Sopenharmony_ci }, 31462306a36Sopenharmony_ci }, 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * per-queue, driver-specific private data. 31962306a36Sopenharmony_ci * there is one source queue and one destination queue for each m2m context. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistruct vpe_q_data { 32262306a36Sopenharmony_ci /* current v4l2 format info */ 32362306a36Sopenharmony_ci struct v4l2_format format; 32462306a36Sopenharmony_ci unsigned int flags; 32562306a36Sopenharmony_ci struct v4l2_rect c_rect; /* crop/compose rectangle */ 32662306a36Sopenharmony_ci struct vpe_fmt *fmt; /* format info */ 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* vpe_q_data flag bits */ 33062306a36Sopenharmony_ci#define Q_DATA_FRAME_1D BIT(0) 33162306a36Sopenharmony_ci#define Q_DATA_MODE_TILED BIT(1) 33262306a36Sopenharmony_ci#define Q_DATA_INTERLACED_ALTERNATE BIT(2) 33362306a36Sopenharmony_ci#define Q_DATA_INTERLACED_SEQ_TB BIT(3) 33462306a36Sopenharmony_ci#define Q_DATA_INTERLACED_SEQ_BT BIT(4) 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci#define Q_IS_SEQ_XX (Q_DATA_INTERLACED_SEQ_TB | \ 33762306a36Sopenharmony_ci Q_DATA_INTERLACED_SEQ_BT) 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \ 34062306a36Sopenharmony_ci Q_DATA_INTERLACED_SEQ_TB | \ 34162306a36Sopenharmony_ci Q_DATA_INTERLACED_SEQ_BT) 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cienum { 34462306a36Sopenharmony_ci Q_DATA_SRC = 0, 34562306a36Sopenharmony_ci Q_DATA_DST = 1, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* find our format description corresponding to the passed v4l2_format */ 34962306a36Sopenharmony_cistatic struct vpe_fmt *__find_format(u32 fourcc) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct vpe_fmt *fmt; 35262306a36Sopenharmony_ci unsigned int k; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) { 35562306a36Sopenharmony_ci fmt = &vpe_formats[k]; 35662306a36Sopenharmony_ci if (fmt->fourcc == fourcc) 35762306a36Sopenharmony_ci return fmt; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct vpe_fmt *find_format(struct v4l2_format *f) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return __find_format(f->fmt.pix.pixelformat); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * there is one vpe_dev structure in the driver, it is shared by 37062306a36Sopenharmony_ci * all instances. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistruct vpe_dev { 37362306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 37462306a36Sopenharmony_ci struct video_device vfd; 37562306a36Sopenharmony_ci struct v4l2_m2m_dev *m2m_dev; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci atomic_t num_instances; /* count of driver instances */ 37862306a36Sopenharmony_ci dma_addr_t loaded_mmrs; /* shadow mmrs in device */ 37962306a36Sopenharmony_ci struct mutex dev_mutex; 38062306a36Sopenharmony_ci spinlock_t lock; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci int irq; 38362306a36Sopenharmony_ci void __iomem *base; 38462306a36Sopenharmony_ci struct resource *res; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci struct vpdma_data vpdma_data; 38762306a36Sopenharmony_ci struct vpdma_data *vpdma; /* vpdma data handle */ 38862306a36Sopenharmony_ci struct sc_data *sc; /* scaler data handle */ 38962306a36Sopenharmony_ci struct csc_data *csc; /* csc data handle */ 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * There is one vpe_ctx structure for each m2m context. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistruct vpe_ctx { 39662306a36Sopenharmony_ci struct v4l2_fh fh; 39762306a36Sopenharmony_ci struct vpe_dev *dev; 39862306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci unsigned int field; /* current field */ 40162306a36Sopenharmony_ci unsigned int sequence; /* current frame/field seq */ 40262306a36Sopenharmony_ci unsigned int aborting; /* abort after next irq */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci unsigned int bufs_per_job; /* input buffers per batch */ 40562306a36Sopenharmony_ci unsigned int bufs_completed; /* bufs done in this batch */ 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci struct vpe_q_data q_data[2]; /* src & dst queue data */ 40862306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; 40962306a36Sopenharmony_ci struct vb2_v4l2_buffer *dst_vb; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */ 41262306a36Sopenharmony_ci void *mv_buf[2]; /* virtual addrs of motion vector bufs */ 41362306a36Sopenharmony_ci size_t mv_buf_size; /* current motion vector buffer size */ 41462306a36Sopenharmony_ci struct vpdma_buf mmr_adb; /* shadow reg addr/data block */ 41562306a36Sopenharmony_ci struct vpdma_buf sc_coeff_h; /* h coeff buffer */ 41662306a36Sopenharmony_ci struct vpdma_buf sc_coeff_v; /* v coeff buffer */ 41762306a36Sopenharmony_ci struct vpdma_desc_list desc_list; /* DMA descriptor list */ 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci bool deinterlacing; /* using de-interlacer */ 42062306a36Sopenharmony_ci bool load_mmrs; /* have new shadow reg values */ 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci unsigned int src_mv_buf_selector; 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* 42762306a36Sopenharmony_ci * M2M devices get 2 queues. 42862306a36Sopenharmony_ci * Return the queue given the type. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, 43162306a36Sopenharmony_ci enum v4l2_buf_type type) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci switch (type) { 43462306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: 43562306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 43662306a36Sopenharmony_ci return &ctx->q_data[Q_DATA_SRC]; 43762306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: 43862306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 43962306a36Sopenharmony_ci return &ctx->q_data[Q_DATA_DST]; 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci return NULL; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci return NULL; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic u32 read_reg(struct vpe_dev *dev, int offset) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci return ioread32(dev->base + offset); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void write_reg(struct vpe_dev *dev, int offset, u32 value) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci iowrite32(value, dev->base + offset); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* register field read/write helpers */ 45762306a36Sopenharmony_cistatic int get_field(u32 value, u32 mask, int shift) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci return (value & (mask << shift)) >> shift; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci return get_field(read_reg(dev, offset), mask, shift); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void write_field(u32 *valp, u32 field, u32 mask, int shift) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci u32 val = *valp; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci val &= ~(mask << shift); 47262306a36Sopenharmony_ci val |= (field & mask) << shift; 47362306a36Sopenharmony_ci *valp = val; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void write_field_reg(struct vpe_dev *dev, int offset, u32 field, 47762306a36Sopenharmony_ci u32 mask, int shift) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci u32 val = read_reg(dev, offset); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci write_field(&val, field, mask, shift); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci write_reg(dev, offset, val); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* 48762306a36Sopenharmony_ci * DMA address/data block for the shadow registers 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistruct vpe_mmr_adb { 49062306a36Sopenharmony_ci struct vpdma_adb_hdr out_fmt_hdr; 49162306a36Sopenharmony_ci u32 out_fmt_reg[1]; 49262306a36Sopenharmony_ci u32 out_fmt_pad[3]; 49362306a36Sopenharmony_ci struct vpdma_adb_hdr us1_hdr; 49462306a36Sopenharmony_ci u32 us1_regs[8]; 49562306a36Sopenharmony_ci struct vpdma_adb_hdr us2_hdr; 49662306a36Sopenharmony_ci u32 us2_regs[8]; 49762306a36Sopenharmony_ci struct vpdma_adb_hdr us3_hdr; 49862306a36Sopenharmony_ci u32 us3_regs[8]; 49962306a36Sopenharmony_ci struct vpdma_adb_hdr dei_hdr; 50062306a36Sopenharmony_ci u32 dei_regs[8]; 50162306a36Sopenharmony_ci struct vpdma_adb_hdr sc_hdr0; 50262306a36Sopenharmony_ci u32 sc_regs0[7]; 50362306a36Sopenharmony_ci u32 sc_pad0[1]; 50462306a36Sopenharmony_ci struct vpdma_adb_hdr sc_hdr8; 50562306a36Sopenharmony_ci u32 sc_regs8[6]; 50662306a36Sopenharmony_ci u32 sc_pad8[2]; 50762306a36Sopenharmony_ci struct vpdma_adb_hdr sc_hdr17; 50862306a36Sopenharmony_ci u32 sc_regs17[9]; 50962306a36Sopenharmony_ci u32 sc_pad17[3]; 51062306a36Sopenharmony_ci struct vpdma_adb_hdr csc_hdr; 51162306a36Sopenharmony_ci u32 csc_regs[6]; 51262306a36Sopenharmony_ci u32 csc_pad[2]; 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci#define GET_OFFSET_TOP(ctx, obj, reg) \ 51662306a36Sopenharmony_ci ((obj)->res->start - ctx->dev->res->start + reg) 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \ 51962306a36Sopenharmony_ci VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) 52062306a36Sopenharmony_ci/* 52162306a36Sopenharmony_ci * Set the headers for all of the address/data block structures. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_cistatic void init_adb_hdrs(struct vpe_ctx *ctx) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT); 52662306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0); 52762306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); 52862306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); 52962306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); 53062306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, 53162306a36Sopenharmony_ci GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); 53262306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, 53362306a36Sopenharmony_ci GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); 53462306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, 53562306a36Sopenharmony_ci GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); 53662306a36Sopenharmony_ci VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, 53762306a36Sopenharmony_ci GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* 54162306a36Sopenharmony_ci * Allocate or re-allocate the motion vector DMA buffers 54262306a36Sopenharmony_ci * There are two buffers, one for input and one for output. 54362306a36Sopenharmony_ci * However, the roles are reversed after each field is processed. 54462306a36Sopenharmony_ci * In other words, after each field is processed, the previous 54562306a36Sopenharmony_ci * output (dst) MV buffer becomes the new input (src) MV buffer. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_cistatic int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct device *dev = ctx->dev->v4l2_dev.dev; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (ctx->mv_buf_size == size) 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (ctx->mv_buf[0]) 55562306a36Sopenharmony_ci dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0], 55662306a36Sopenharmony_ci ctx->mv_buf_dma[0]); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (ctx->mv_buf[1]) 55962306a36Sopenharmony_ci dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1], 56062306a36Sopenharmony_ci ctx->mv_buf_dma[1]); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (size == 0) 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0], 56662306a36Sopenharmony_ci GFP_KERNEL); 56762306a36Sopenharmony_ci if (!ctx->mv_buf[0]) { 56862306a36Sopenharmony_ci vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); 56962306a36Sopenharmony_ci return -ENOMEM; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1], 57362306a36Sopenharmony_ci GFP_KERNEL); 57462306a36Sopenharmony_ci if (!ctx->mv_buf[1]) { 57562306a36Sopenharmony_ci vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); 57662306a36Sopenharmony_ci dma_free_coherent(dev, size, ctx->mv_buf[0], 57762306a36Sopenharmony_ci ctx->mv_buf_dma[0]); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return -ENOMEM; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ctx->mv_buf_size = size; 58362306a36Sopenharmony_ci ctx->src_mv_buf_selector = 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void free_mv_buffers(struct vpe_ctx *ctx) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci realloc_mv_buffers(ctx, 0); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * While de-interlacing, we keep the two most recent input buffers 59562306a36Sopenharmony_ci * around. This function frees those two buffers when we have 59662306a36Sopenharmony_ci * finished processing the current stream. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_cistatic void free_vbs(struct vpe_ctx *ctx) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct vpe_dev *dev = ctx->dev; 60162306a36Sopenharmony_ci unsigned long flags; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (ctx->src_vbs[2] == NULL) 60462306a36Sopenharmony_ci return; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 60762306a36Sopenharmony_ci if (ctx->src_vbs[2]) { 60862306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE); 60962306a36Sopenharmony_ci if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) 61062306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); 61162306a36Sopenharmony_ci ctx->src_vbs[2] = NULL; 61262306a36Sopenharmony_ci ctx->src_vbs[1] = NULL; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * Enable or disable the VPE clocks 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_cistatic void vpe_set_clock_enable(struct vpe_dev *dev, bool on) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci u32 val = 0; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (on) 62562306a36Sopenharmony_ci val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE; 62662306a36Sopenharmony_ci write_reg(dev, VPE_CLK_ENABLE, val); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void vpe_top_reset(struct vpe_dev *dev) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK, 63362306a36Sopenharmony_ci VPE_DATA_PATH_CLK_RESET_SHIFT); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci usleep_range(100, 150); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK, 63862306a36Sopenharmony_ci VPE_DATA_PATH_CLK_RESET_SHIFT); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void vpe_top_vpdma_reset(struct vpe_dev *dev) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK, 64462306a36Sopenharmony_ci VPE_VPDMA_CLK_RESET_SHIFT); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci usleep_range(100, 150); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK, 64962306a36Sopenharmony_ci VPE_VPDMA_CLK_RESET_SHIFT); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/* 65362306a36Sopenharmony_ci * Load the correct of upsampler coefficients into the shadow MMRs 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic void set_us_coefficients(struct vpe_ctx *ctx) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 65862306a36Sopenharmony_ci struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; 65962306a36Sopenharmony_ci u32 *us1_reg = &mmr_adb->us1_regs[0]; 66062306a36Sopenharmony_ci u32 *us2_reg = &mmr_adb->us2_regs[0]; 66162306a36Sopenharmony_ci u32 *us3_reg = &mmr_adb->us3_regs[0]; 66262306a36Sopenharmony_ci const unsigned short *cp, *end_cp; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci cp = &us_coeffs[0].anchor_fid0_c0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (s_q_data->flags & Q_IS_INTERLACED) /* interlaced */ 66762306a36Sopenharmony_ci cp += sizeof(us_coeffs[0]) / sizeof(*cp); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci while (cp < end_cp) { 67262306a36Sopenharmony_ci write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT); 67362306a36Sopenharmony_ci write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT); 67462306a36Sopenharmony_ci *us2_reg++ = *us1_reg; 67562306a36Sopenharmony_ci *us3_reg++ = *us1_reg++; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci ctx->load_mmrs = true; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/* 68162306a36Sopenharmony_ci * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_cistatic void set_cfg_modes(struct vpe_ctx *ctx) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; 68662306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 68762306a36Sopenharmony_ci u32 *us1_reg0 = &mmr_adb->us1_regs[0]; 68862306a36Sopenharmony_ci u32 *us2_reg0 = &mmr_adb->us2_regs[0]; 68962306a36Sopenharmony_ci u32 *us3_reg0 = &mmr_adb->us3_regs[0]; 69062306a36Sopenharmony_ci int cfg_mode = 1; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* 69362306a36Sopenharmony_ci * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing. 69462306a36Sopenharmony_ci * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (fmt->fourcc == V4L2_PIX_FMT_NV12 || 69862306a36Sopenharmony_ci fmt->fourcc == V4L2_PIX_FMT_NV21) 69962306a36Sopenharmony_ci cfg_mode = 0; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); 70262306a36Sopenharmony_ci write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); 70362306a36Sopenharmony_ci write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ctx->load_mmrs = true; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic void set_line_modes(struct vpe_ctx *ctx) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; 71162306a36Sopenharmony_ci int line_mode = 1; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (fmt->fourcc == V4L2_PIX_FMT_NV12 || 71462306a36Sopenharmony_ci fmt->fourcc == V4L2_PIX_FMT_NV21) 71562306a36Sopenharmony_ci line_mode = 0; /* double lines to line buffer */ 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* regs for now */ 71862306a36Sopenharmony_ci vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN); 71962306a36Sopenharmony_ci vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN); 72062306a36Sopenharmony_ci vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* frame start for input luma */ 72362306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 72462306a36Sopenharmony_ci VPE_CHAN_LUMA1_IN); 72562306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 72662306a36Sopenharmony_ci VPE_CHAN_LUMA2_IN); 72762306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 72862306a36Sopenharmony_ci VPE_CHAN_LUMA3_IN); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* frame start for input chroma */ 73162306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 73262306a36Sopenharmony_ci VPE_CHAN_CHROMA1_IN); 73362306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 73462306a36Sopenharmony_ci VPE_CHAN_CHROMA2_IN); 73562306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 73662306a36Sopenharmony_ci VPE_CHAN_CHROMA3_IN); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* frame start for MV in client */ 73962306a36Sopenharmony_ci vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, 74062306a36Sopenharmony_ci VPE_CHAN_MV_IN); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci/* 74462306a36Sopenharmony_ci * Set the shadow registers that are modified when the source 74562306a36Sopenharmony_ci * format changes. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_cistatic void set_src_registers(struct vpe_ctx *ctx) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci set_us_coefficients(ctx); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/* 75362306a36Sopenharmony_ci * Set the shadow registers that are modified when the destination 75462306a36Sopenharmony_ci * format changes. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic void set_dst_registers(struct vpe_ctx *ctx) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 75962306a36Sopenharmony_ci struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; 76062306a36Sopenharmony_ci const struct v4l2_format_info *finfo; 76162306a36Sopenharmony_ci u32 val = 0; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci finfo = v4l2_format_info(fmt->fourcc); 76462306a36Sopenharmony_ci if (v4l2_is_format_rgb(finfo)) { 76562306a36Sopenharmony_ci val |= VPE_RGB_OUT_SELECT; 76662306a36Sopenharmony_ci vpdma_set_bg_color(ctx->dev->vpdma, 76762306a36Sopenharmony_ci (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); 76862306a36Sopenharmony_ci } else if (fmt->fourcc == V4L2_PIX_FMT_NV16) 76962306a36Sopenharmony_ci val |= VPE_COLOR_SEPARATE_422; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* 77262306a36Sopenharmony_ci * the source of CHR_DS and CSC is always the scaler, irrespective of 77362306a36Sopenharmony_ci * whether it's used or not 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (fmt->fourcc != V4L2_PIX_FMT_NV12 && 77862306a36Sopenharmony_ci fmt->fourcc != V4L2_PIX_FMT_NV21) 77962306a36Sopenharmony_ci val |= VPE_DS_BYPASS; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci mmr_adb->out_fmt_reg[0] = val; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ctx->load_mmrs = true; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/* 78762306a36Sopenharmony_ci * Set the de-interlacer shadow register values 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_cistatic void set_dei_regs(struct vpe_ctx *ctx) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 79262306a36Sopenharmony_ci struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; 79362306a36Sopenharmony_ci unsigned int src_h = s_q_data->c_rect.height; 79462306a36Sopenharmony_ci unsigned int src_w = s_q_data->c_rect.width; 79562306a36Sopenharmony_ci u32 *dei_mmr0 = &mmr_adb->dei_regs[0]; 79662306a36Sopenharmony_ci bool deinterlace = true; 79762306a36Sopenharmony_ci u32 val = 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * according to TRM, we should set DEI in progressive bypass mode when 80162306a36Sopenharmony_ci * the input content is progressive, however, DEI is bypassed correctly 80262306a36Sopenharmony_ci * for both progressive and interlace content in interlace bypass mode. 80362306a36Sopenharmony_ci * It has been recommended not to use progressive bypass mode. 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) { 80662306a36Sopenharmony_ci deinterlace = false; 80762306a36Sopenharmony_ci val = VPE_DEI_INTERLACE_BYPASS; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci src_h = deinterlace ? src_h * 2 : src_h; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci val |= (src_h << VPE_DEI_HEIGHT_SHIFT) | 81362306a36Sopenharmony_ci (src_w << VPE_DEI_WIDTH_SHIFT) | 81462306a36Sopenharmony_ci VPE_DEI_FIELD_FLUSH; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci *dei_mmr0 = val; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci ctx->load_mmrs = true; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic void set_dei_shadow_registers(struct vpe_ctx *ctx) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 82462306a36Sopenharmony_ci u32 *dei_mmr = &mmr_adb->dei_regs[0]; 82562306a36Sopenharmony_ci const struct vpe_dei_regs *cur = &dei_regs; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci dei_mmr[2] = cur->mdt_spacial_freq_thr_reg; 82862306a36Sopenharmony_ci dei_mmr[3] = cur->edi_config_reg; 82962306a36Sopenharmony_ci dei_mmr[4] = cur->edi_lut_reg0; 83062306a36Sopenharmony_ci dei_mmr[5] = cur->edi_lut_reg1; 83162306a36Sopenharmony_ci dei_mmr[6] = cur->edi_lut_reg2; 83262306a36Sopenharmony_ci dei_mmr[7] = cur->edi_lut_reg3; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ctx->load_mmrs = true; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic void config_edi_input_mode(struct vpe_ctx *ctx, int mode) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 84062306a36Sopenharmony_ci u32 *edi_config_reg = &mmr_adb->dei_regs[3]; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (mode & 0x2) 84362306a36Sopenharmony_ci write_field(edi_config_reg, 1, 1, 2); /* EDI_ENABLE_3D */ 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (mode & 0x3) 84662306a36Sopenharmony_ci write_field(edi_config_reg, 1, 1, 3); /* EDI_CHROMA_3D */ 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci write_field(edi_config_reg, mode, VPE_EDI_INP_MODE_MASK, 84962306a36Sopenharmony_ci VPE_EDI_INP_MODE_SHIFT); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ctx->load_mmrs = true; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* 85562306a36Sopenharmony_ci * Set the shadow registers whose values are modified when either the 85662306a36Sopenharmony_ci * source or destination format is changed. 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_cistatic int set_srcdst_params(struct vpe_ctx *ctx) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; 86162306a36Sopenharmony_ci struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; 86262306a36Sopenharmony_ci struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; 86362306a36Sopenharmony_ci unsigned int src_w = s_q_data->c_rect.width; 86462306a36Sopenharmony_ci unsigned int src_h = s_q_data->c_rect.height; 86562306a36Sopenharmony_ci unsigned int dst_w = d_q_data->c_rect.width; 86662306a36Sopenharmony_ci unsigned int dst_h = d_q_data->c_rect.height; 86762306a36Sopenharmony_ci struct v4l2_pix_format_mplane *spix; 86862306a36Sopenharmony_ci size_t mv_buf_size; 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci ctx->sequence = 0; 87262306a36Sopenharmony_ci ctx->field = V4L2_FIELD_TOP; 87362306a36Sopenharmony_ci spix = &s_q_data->format.fmt.pix_mp; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if ((s_q_data->flags & Q_IS_INTERLACED) && 87662306a36Sopenharmony_ci !(d_q_data->flags & Q_IS_INTERLACED)) { 87762306a36Sopenharmony_ci int bytes_per_line; 87862306a36Sopenharmony_ci const struct vpdma_data_format *mv = 87962306a36Sopenharmony_ci &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* 88262306a36Sopenharmony_ci * we make sure that the source image has a 16 byte aligned 88362306a36Sopenharmony_ci * stride, we need to do the same for the motion vector buffer 88462306a36Sopenharmony_ci * by aligning it's stride to the next 16 byte boundary. this 88562306a36Sopenharmony_ci * extra space will not be used by the de-interlacer, but will 88662306a36Sopenharmony_ci * ensure that vpdma operates correctly 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci bytes_per_line = ALIGN((spix->width * mv->depth) >> 3, 88962306a36Sopenharmony_ci VPDMA_STRIDE_ALIGN); 89062306a36Sopenharmony_ci mv_buf_size = bytes_per_line * spix->height; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ctx->deinterlacing = true; 89362306a36Sopenharmony_ci src_h <<= 1; 89462306a36Sopenharmony_ci } else { 89562306a36Sopenharmony_ci ctx->deinterlacing = false; 89662306a36Sopenharmony_ci mv_buf_size = 0; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci free_vbs(ctx); 90062306a36Sopenharmony_ci ctx->src_vbs[2] = ctx->src_vbs[1] = ctx->src_vbs[0] = NULL; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci ret = realloc_mv_buffers(ctx, mv_buf_size); 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci set_cfg_modes(ctx); 90762306a36Sopenharmony_ci set_dei_regs(ctx); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], 91062306a36Sopenharmony_ci &s_q_data->format, &d_q_data->format); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); 91362306a36Sopenharmony_ci sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], 91662306a36Sopenharmony_ci &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], 91762306a36Sopenharmony_ci src_w, src_h, dst_w, dst_h); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci/* 92362306a36Sopenharmony_ci * mem2mem callbacks 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * job_ready() - check whether an instance is ready to be scheduled to run 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_cistatic int job_ready(void *priv) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct vpe_ctx *ctx = priv; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * This check is needed as this might be called directly from driver 93562306a36Sopenharmony_ci * When called by m2m framework, this will always satisfy, but when 93662306a36Sopenharmony_ci * called from vpe_irq, this might fail. (src stream with zero buffers) 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 || 93962306a36Sopenharmony_ci v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0) 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci return 1; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic void job_abort(void *priv) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct vpe_ctx *ctx = priv; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Will cancel the transaction in the next interrupt handler */ 95062306a36Sopenharmony_ci ctx->aborting = 1; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void vpe_dump_regs(struct vpe_dev *dev) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci vpe_dbg(dev, "VPE Registers:\n"); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci DUMPREG(PID); 96062306a36Sopenharmony_ci DUMPREG(SYSCONFIG); 96162306a36Sopenharmony_ci DUMPREG(INT0_STATUS0_RAW); 96262306a36Sopenharmony_ci DUMPREG(INT0_STATUS0); 96362306a36Sopenharmony_ci DUMPREG(INT0_ENABLE0); 96462306a36Sopenharmony_ci DUMPREG(INT0_STATUS1_RAW); 96562306a36Sopenharmony_ci DUMPREG(INT0_STATUS1); 96662306a36Sopenharmony_ci DUMPREG(INT0_ENABLE1); 96762306a36Sopenharmony_ci DUMPREG(CLK_ENABLE); 96862306a36Sopenharmony_ci DUMPREG(CLK_RESET); 96962306a36Sopenharmony_ci DUMPREG(CLK_FORMAT_SELECT); 97062306a36Sopenharmony_ci DUMPREG(CLK_RANGE_MAP); 97162306a36Sopenharmony_ci DUMPREG(US1_R0); 97262306a36Sopenharmony_ci DUMPREG(US1_R1); 97362306a36Sopenharmony_ci DUMPREG(US1_R2); 97462306a36Sopenharmony_ci DUMPREG(US1_R3); 97562306a36Sopenharmony_ci DUMPREG(US1_R4); 97662306a36Sopenharmony_ci DUMPREG(US1_R5); 97762306a36Sopenharmony_ci DUMPREG(US1_R6); 97862306a36Sopenharmony_ci DUMPREG(US1_R7); 97962306a36Sopenharmony_ci DUMPREG(US2_R0); 98062306a36Sopenharmony_ci DUMPREG(US2_R1); 98162306a36Sopenharmony_ci DUMPREG(US2_R2); 98262306a36Sopenharmony_ci DUMPREG(US2_R3); 98362306a36Sopenharmony_ci DUMPREG(US2_R4); 98462306a36Sopenharmony_ci DUMPREG(US2_R5); 98562306a36Sopenharmony_ci DUMPREG(US2_R6); 98662306a36Sopenharmony_ci DUMPREG(US2_R7); 98762306a36Sopenharmony_ci DUMPREG(US3_R0); 98862306a36Sopenharmony_ci DUMPREG(US3_R1); 98962306a36Sopenharmony_ci DUMPREG(US3_R2); 99062306a36Sopenharmony_ci DUMPREG(US3_R3); 99162306a36Sopenharmony_ci DUMPREG(US3_R4); 99262306a36Sopenharmony_ci DUMPREG(US3_R5); 99362306a36Sopenharmony_ci DUMPREG(US3_R6); 99462306a36Sopenharmony_ci DUMPREG(US3_R7); 99562306a36Sopenharmony_ci DUMPREG(DEI_FRAME_SIZE); 99662306a36Sopenharmony_ci DUMPREG(MDT_BYPASS); 99762306a36Sopenharmony_ci DUMPREG(MDT_SF_THRESHOLD); 99862306a36Sopenharmony_ci DUMPREG(EDI_CONFIG); 99962306a36Sopenharmony_ci DUMPREG(DEI_EDI_LUT_R0); 100062306a36Sopenharmony_ci DUMPREG(DEI_EDI_LUT_R1); 100162306a36Sopenharmony_ci DUMPREG(DEI_EDI_LUT_R2); 100262306a36Sopenharmony_ci DUMPREG(DEI_EDI_LUT_R3); 100362306a36Sopenharmony_ci DUMPREG(DEI_FMD_WINDOW_R0); 100462306a36Sopenharmony_ci DUMPREG(DEI_FMD_WINDOW_R1); 100562306a36Sopenharmony_ci DUMPREG(DEI_FMD_CONTROL_R0); 100662306a36Sopenharmony_ci DUMPREG(DEI_FMD_CONTROL_R1); 100762306a36Sopenharmony_ci DUMPREG(DEI_FMD_STATUS_R0); 100862306a36Sopenharmony_ci DUMPREG(DEI_FMD_STATUS_R1); 100962306a36Sopenharmony_ci DUMPREG(DEI_FMD_STATUS_R2); 101062306a36Sopenharmony_ci#undef DUMPREG 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci sc_dump_regs(dev->sc); 101362306a36Sopenharmony_ci csc_dump_regs(dev->csc); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic void add_out_dtd(struct vpe_ctx *ctx, int port) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; 101962306a36Sopenharmony_ci const struct vpe_port_data *p_data = &port_data[port]; 102062306a36Sopenharmony_ci struct vb2_buffer *vb = &ctx->dst_vb->vb2_buf; 102162306a36Sopenharmony_ci struct vpe_fmt *fmt = q_data->fmt; 102262306a36Sopenharmony_ci const struct vpdma_data_format *vpdma_fmt; 102362306a36Sopenharmony_ci int mv_buf_selector = !ctx->src_mv_buf_selector; 102462306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 102562306a36Sopenharmony_ci dma_addr_t dma_addr; 102662306a36Sopenharmony_ci u32 flags = 0; 102762306a36Sopenharmony_ci u32 offset = 0; 102862306a36Sopenharmony_ci u32 stride; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (port == VPE_PORT_MV_OUT) { 103162306a36Sopenharmony_ci vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; 103262306a36Sopenharmony_ci dma_addr = ctx->mv_buf_dma[mv_buf_selector]; 103362306a36Sopenharmony_ci q_data = &ctx->q_data[Q_DATA_SRC]; 103462306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 103562306a36Sopenharmony_ci stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, 103662306a36Sopenharmony_ci VPDMA_STRIDE_ALIGN); 103762306a36Sopenharmony_ci } else { 103862306a36Sopenharmony_ci /* to incorporate interleaved formats */ 103962306a36Sopenharmony_ci int plane = fmt->coplanar ? p_data->vb_part : 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 104262306a36Sopenharmony_ci vpdma_fmt = fmt->vpdma_fmt[plane]; 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * If we are using a single plane buffer and 104562306a36Sopenharmony_ci * we need to set a separate vpdma chroma channel. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ci if (pix->num_planes == 1 && plane) { 104862306a36Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 104962306a36Sopenharmony_ci /* Compute required offset */ 105062306a36Sopenharmony_ci offset = pix->plane_fmt[0].bytesperline * pix->height; 105162306a36Sopenharmony_ci } else { 105262306a36Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); 105362306a36Sopenharmony_ci /* Use address as is, no offset */ 105462306a36Sopenharmony_ci offset = 0; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci if (!dma_addr) { 105762306a36Sopenharmony_ci vpe_err(ctx->dev, 105862306a36Sopenharmony_ci "acquiring output buffer(%d) dma_addr failed\n", 105962306a36Sopenharmony_ci port); 106062306a36Sopenharmony_ci return; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci /* Apply the offset */ 106362306a36Sopenharmony_ci dma_addr += offset; 106462306a36Sopenharmony_ci stride = pix->plane_fmt[VPE_LUMA].bytesperline; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (q_data->flags & Q_DATA_FRAME_1D) 106862306a36Sopenharmony_ci flags |= VPDMA_DATA_FRAME_1D; 106962306a36Sopenharmony_ci if (q_data->flags & Q_DATA_MODE_TILED) 107062306a36Sopenharmony_ci flags |= VPDMA_DATA_MODE_TILED; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1, 107362306a36Sopenharmony_ci MAX_W, MAX_H); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci vpdma_add_out_dtd(&ctx->desc_list, pix->width, 107662306a36Sopenharmony_ci stride, &q_data->c_rect, 107762306a36Sopenharmony_ci vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, 107862306a36Sopenharmony_ci MAX_OUT_HEIGHT_REG1, p_data->channel, flags); 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic void add_in_dtd(struct vpe_ctx *ctx, int port) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; 108462306a36Sopenharmony_ci const struct vpe_port_data *p_data = &port_data[port]; 108562306a36Sopenharmony_ci struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf; 108662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 108762306a36Sopenharmony_ci struct vpe_fmt *fmt = q_data->fmt; 108862306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 108962306a36Sopenharmony_ci const struct vpdma_data_format *vpdma_fmt; 109062306a36Sopenharmony_ci int mv_buf_selector = ctx->src_mv_buf_selector; 109162306a36Sopenharmony_ci int field = vbuf->field == V4L2_FIELD_BOTTOM; 109262306a36Sopenharmony_ci int frame_width, frame_height; 109362306a36Sopenharmony_ci dma_addr_t dma_addr; 109462306a36Sopenharmony_ci u32 flags = 0; 109562306a36Sopenharmony_ci u32 offset = 0; 109662306a36Sopenharmony_ci u32 stride; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 109962306a36Sopenharmony_ci if (port == VPE_PORT_MV_IN) { 110062306a36Sopenharmony_ci vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; 110162306a36Sopenharmony_ci dma_addr = ctx->mv_buf_dma[mv_buf_selector]; 110262306a36Sopenharmony_ci stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, 110362306a36Sopenharmony_ci VPDMA_STRIDE_ALIGN); 110462306a36Sopenharmony_ci } else { 110562306a36Sopenharmony_ci /* to incorporate interleaved formats */ 110662306a36Sopenharmony_ci int plane = fmt->coplanar ? p_data->vb_part : 0; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci vpdma_fmt = fmt->vpdma_fmt[plane]; 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * If we are using a single plane buffer and 111162306a36Sopenharmony_ci * we need to set a separate vpdma chroma channel. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci if (pix->num_planes == 1 && plane) { 111462306a36Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 111562306a36Sopenharmony_ci /* Compute required offset */ 111662306a36Sopenharmony_ci offset = pix->plane_fmt[0].bytesperline * pix->height; 111762306a36Sopenharmony_ci } else { 111862306a36Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); 111962306a36Sopenharmony_ci /* Use address as is, no offset */ 112062306a36Sopenharmony_ci offset = 0; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci if (!dma_addr) { 112362306a36Sopenharmony_ci vpe_err(ctx->dev, 112462306a36Sopenharmony_ci "acquiring output buffer(%d) dma_addr failed\n", 112562306a36Sopenharmony_ci port); 112662306a36Sopenharmony_ci return; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci /* Apply the offset */ 112962306a36Sopenharmony_ci dma_addr += offset; 113062306a36Sopenharmony_ci stride = pix->plane_fmt[VPE_LUMA].bytesperline; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * field used in VPDMA desc = 0 (top) / 1 (bottom) 113462306a36Sopenharmony_ci * Use top or bottom field from same vb alternately 113562306a36Sopenharmony_ci * For each de-interlacing operation, f,f-1,f-2 should be one 113662306a36Sopenharmony_ci * of TBT or BTB 113762306a36Sopenharmony_ci */ 113862306a36Sopenharmony_ci if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB || 113962306a36Sopenharmony_ci q_data->flags & Q_DATA_INTERLACED_SEQ_BT) { 114062306a36Sopenharmony_ci /* Select initial value based on format */ 114162306a36Sopenharmony_ci if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT) 114262306a36Sopenharmony_ci field = 1; 114362306a36Sopenharmony_ci else 114462306a36Sopenharmony_ci field = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Toggle for each vb_index and each operation */ 114762306a36Sopenharmony_ci field = (field + p_data->vb_index + ctx->sequence) % 2; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (field) { 115062306a36Sopenharmony_ci int height = pix->height / 2; 115162306a36Sopenharmony_ci int bpp; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (fmt->fourcc == V4L2_PIX_FMT_NV12 || 115462306a36Sopenharmony_ci fmt->fourcc == V4L2_PIX_FMT_NV21) 115562306a36Sopenharmony_ci bpp = 1; 115662306a36Sopenharmony_ci else 115762306a36Sopenharmony_ci bpp = vpdma_fmt->depth >> 3; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (plane) 116062306a36Sopenharmony_ci height /= 2; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci dma_addr += pix->width * height * bpp; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (q_data->flags & Q_DATA_FRAME_1D) 116862306a36Sopenharmony_ci flags |= VPDMA_DATA_FRAME_1D; 116962306a36Sopenharmony_ci if (q_data->flags & Q_DATA_MODE_TILED) 117062306a36Sopenharmony_ci flags |= VPDMA_DATA_MODE_TILED; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci frame_width = q_data->c_rect.width; 117362306a36Sopenharmony_ci frame_height = q_data->c_rect.height; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 || 117662306a36Sopenharmony_ci fmt->fourcc == V4L2_PIX_FMT_NV21)) 117762306a36Sopenharmony_ci frame_height /= 2; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci vpdma_add_in_dtd(&ctx->desc_list, pix->width, stride, 118062306a36Sopenharmony_ci &q_data->c_rect, vpdma_fmt, dma_addr, 118162306a36Sopenharmony_ci p_data->channel, field, flags, frame_width, 118262306a36Sopenharmony_ci frame_height, 0, 0); 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/* 118662306a36Sopenharmony_ci * Enable the expected IRQ sources 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_cistatic void enable_irqs(struct vpe_ctx *ctx) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE); 119162306a36Sopenharmony_ci write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT | 119262306a36Sopenharmony_ci VPE_DS1_UV_ERROR_INT); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, true); 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic void disable_irqs(struct vpe_ctx *ctx) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff); 120062306a36Sopenharmony_ci write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, false); 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci/* device_run() - prepares and starts the device 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * This function is only called when both the source and destination 120862306a36Sopenharmony_ci * buffers are in place. 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_cistatic void device_run(void *priv) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci struct vpe_ctx *ctx = priv; 121362306a36Sopenharmony_ci struct sc_data *sc = ctx->dev->sc; 121462306a36Sopenharmony_ci struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; 121562306a36Sopenharmony_ci struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; 121662306a36Sopenharmony_ci const struct v4l2_format_info *d_finfo; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci d_finfo = v4l2_format_info(d_q_data->fmt->fourcc); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && 122162306a36Sopenharmony_ci ctx->sequence % 2 == 0) { 122262306a36Sopenharmony_ci /* When using SEQ_XX type buffers, each buffer has two fields 122362306a36Sopenharmony_ci * each buffer has two fields (top & bottom) 122462306a36Sopenharmony_ci * Removing one buffer is actually getting two fields 122562306a36Sopenharmony_ci * Alternate between two operations:- 122662306a36Sopenharmony_ci * Even : consume one field but DO NOT REMOVE from queue 122762306a36Sopenharmony_ci * Odd : consume other field and REMOVE from queue 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 123062306a36Sopenharmony_ci WARN_ON(ctx->src_vbs[0] == NULL); 123162306a36Sopenharmony_ci } else { 123262306a36Sopenharmony_ci ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 123362306a36Sopenharmony_ci WARN_ON(ctx->src_vbs[0] == NULL); 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 123762306a36Sopenharmony_ci WARN_ON(ctx->dst_vb == NULL); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (ctx->deinterlacing) { 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (ctx->src_vbs[2] == NULL) { 124262306a36Sopenharmony_ci ctx->src_vbs[2] = ctx->src_vbs[0]; 124362306a36Sopenharmony_ci WARN_ON(ctx->src_vbs[2] == NULL); 124462306a36Sopenharmony_ci ctx->src_vbs[1] = ctx->src_vbs[0]; 124562306a36Sopenharmony_ci WARN_ON(ctx->src_vbs[1] == NULL); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* 124962306a36Sopenharmony_ci * we have output the first 2 frames through line average, we 125062306a36Sopenharmony_ci * now switch to EDI de-interlacer 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_ci if (ctx->sequence == 2) 125362306a36Sopenharmony_ci config_edi_input_mode(ctx, 0x3); /* EDI (Y + UV) */ 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* config descriptors */ 125762306a36Sopenharmony_ci if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) { 125862306a36Sopenharmony_ci vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb); 125962306a36Sopenharmony_ci vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci set_line_modes(ctx); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr; 126462306a36Sopenharmony_ci ctx->load_mmrs = false; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || 126862306a36Sopenharmony_ci sc->load_coeff_h) { 126962306a36Sopenharmony_ci vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); 127062306a36Sopenharmony_ci vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, 127162306a36Sopenharmony_ci &ctx->sc_coeff_h, 0); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; 127462306a36Sopenharmony_ci sc->load_coeff_h = false; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || 127862306a36Sopenharmony_ci sc->load_coeff_v) { 127962306a36Sopenharmony_ci vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); 128062306a36Sopenharmony_ci vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, 128162306a36Sopenharmony_ci &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; 128462306a36Sopenharmony_ci sc->load_coeff_v = false; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* output data descriptors */ 128862306a36Sopenharmony_ci if (ctx->deinterlacing) 128962306a36Sopenharmony_ci add_out_dtd(ctx, VPE_PORT_MV_OUT); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (v4l2_is_format_rgb(d_finfo)) { 129262306a36Sopenharmony_ci add_out_dtd(ctx, VPE_PORT_RGB_OUT); 129362306a36Sopenharmony_ci } else { 129462306a36Sopenharmony_ci add_out_dtd(ctx, VPE_PORT_LUMA_OUT); 129562306a36Sopenharmony_ci if (d_q_data->fmt->coplanar) 129662306a36Sopenharmony_ci add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* input data descriptors */ 130062306a36Sopenharmony_ci if (ctx->deinterlacing) { 130162306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_LUMA3_IN); 130262306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_CHROMA3_IN); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_LUMA2_IN); 130562306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_CHROMA2_IN); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_LUMA1_IN); 130962306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_CHROMA1_IN); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (ctx->deinterlacing) 131262306a36Sopenharmony_ci add_in_dtd(ctx, VPE_PORT_MV_IN); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* sync on channel control descriptors for input ports */ 131562306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN); 131662306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (ctx->deinterlacing) { 131962306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 132062306a36Sopenharmony_ci VPE_CHAN_LUMA2_IN); 132162306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 132262306a36Sopenharmony_ci VPE_CHAN_CHROMA2_IN); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 132562306a36Sopenharmony_ci VPE_CHAN_LUMA3_IN); 132662306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 132762306a36Sopenharmony_ci VPE_CHAN_CHROMA3_IN); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN); 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* sync on channel control descriptors for output ports */ 133362306a36Sopenharmony_ci if (v4l2_is_format_rgb(d_finfo)) { 133462306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 133562306a36Sopenharmony_ci VPE_CHAN_RGB_OUT); 133662306a36Sopenharmony_ci } else { 133762306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 133862306a36Sopenharmony_ci VPE_CHAN_LUMA_OUT); 133962306a36Sopenharmony_ci if (d_q_data->fmt->coplanar) 134062306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, 134162306a36Sopenharmony_ci VPE_CHAN_CHROMA_OUT); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci if (ctx->deinterlacing) 134562306a36Sopenharmony_ci vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci enable_irqs(ctx); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); 135062306a36Sopenharmony_ci vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list, 0); 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic void dei_error(struct vpe_ctx *ctx) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci dev_warn(ctx->dev->v4l2_dev.dev, 135662306a36Sopenharmony_ci "received DEI error interrupt\n"); 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic void ds1_uv_error(struct vpe_ctx *ctx) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci dev_warn(ctx->dev->v4l2_dev.dev, 136262306a36Sopenharmony_ci "received downsampler error interrupt\n"); 136362306a36Sopenharmony_ci} 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic irqreturn_t vpe_irq(int irq_vpe, void *data) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci struct vpe_dev *dev = (struct vpe_dev *)data; 136862306a36Sopenharmony_ci struct vpe_ctx *ctx; 136962306a36Sopenharmony_ci struct vpe_q_data *d_q_data; 137062306a36Sopenharmony_ci struct vb2_v4l2_buffer *s_vb, *d_vb; 137162306a36Sopenharmony_ci unsigned long flags; 137262306a36Sopenharmony_ci u32 irqst0, irqst1; 137362306a36Sopenharmony_ci bool list_complete = false; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci irqst0 = read_reg(dev, VPE_INT0_STATUS0); 137662306a36Sopenharmony_ci if (irqst0) { 137762306a36Sopenharmony_ci write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0); 137862306a36Sopenharmony_ci vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci irqst1 = read_reg(dev, VPE_INT0_STATUS1); 138262306a36Sopenharmony_ci if (irqst1) { 138362306a36Sopenharmony_ci write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1); 138462306a36Sopenharmony_ci vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1); 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); 138862306a36Sopenharmony_ci if (!ctx) { 138962306a36Sopenharmony_ci vpe_err(dev, "instance released before end of transaction\n"); 139062306a36Sopenharmony_ci goto handled; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (irqst1) { 139462306a36Sopenharmony_ci if (irqst1 & VPE_DEI_ERROR_INT) { 139562306a36Sopenharmony_ci irqst1 &= ~VPE_DEI_ERROR_INT; 139662306a36Sopenharmony_ci dei_error(ctx); 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci if (irqst1 & VPE_DS1_UV_ERROR_INT) { 139962306a36Sopenharmony_ci irqst1 &= ~VPE_DS1_UV_ERROR_INT; 140062306a36Sopenharmony_ci ds1_uv_error(ctx); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (irqst0) { 140562306a36Sopenharmony_ci if (irqst0 & VPE_INT0_LIST0_COMPLETE) 140662306a36Sopenharmony_ci vpdma_clear_list_stat(ctx->dev->vpdma, 0, 0); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci irqst0 &= ~(VPE_INT0_LIST0_COMPLETE); 140962306a36Sopenharmony_ci list_complete = true; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (irqst0 | irqst1) { 141362306a36Sopenharmony_ci dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", 141462306a36Sopenharmony_ci irqst0, irqst1); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* 141862306a36Sopenharmony_ci * Setup next operation only when list complete IRQ occurs 141962306a36Sopenharmony_ci * otherwise, skip the following code 142062306a36Sopenharmony_ci */ 142162306a36Sopenharmony_ci if (!list_complete) 142262306a36Sopenharmony_ci goto handled; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci disable_irqs(ctx); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); 142762306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); 142862306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); 142962306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci vpdma_reset_desc_list(&ctx->desc_list); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci /* the previous dst mv buffer becomes the next src mv buffer */ 143462306a36Sopenharmony_ci ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci s_vb = ctx->src_vbs[0]; 143762306a36Sopenharmony_ci d_vb = ctx->dst_vb; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci d_vb->flags = s_vb->flags; 144062306a36Sopenharmony_ci d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE) 144362306a36Sopenharmony_ci d_vb->timecode = s_vb->timecode; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci d_vb->sequence = ctx->sequence; 144662306a36Sopenharmony_ci s_vb->sequence = ctx->sequence; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci d_q_data = &ctx->q_data[Q_DATA_DST]; 144962306a36Sopenharmony_ci if (d_q_data->flags & Q_IS_INTERLACED) { 145062306a36Sopenharmony_ci d_vb->field = ctx->field; 145162306a36Sopenharmony_ci if (ctx->field == V4L2_FIELD_BOTTOM) { 145262306a36Sopenharmony_ci ctx->sequence++; 145362306a36Sopenharmony_ci ctx->field = V4L2_FIELD_TOP; 145462306a36Sopenharmony_ci } else { 145562306a36Sopenharmony_ci WARN_ON(ctx->field != V4L2_FIELD_TOP); 145662306a36Sopenharmony_ci ctx->field = V4L2_FIELD_BOTTOM; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci } else { 145962306a36Sopenharmony_ci d_vb->field = V4L2_FIELD_NONE; 146062306a36Sopenharmony_ci ctx->sequence++; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (ctx->deinterlacing) { 146462306a36Sopenharmony_ci /* 146562306a36Sopenharmony_ci * Allow source buffer to be dequeued only if it won't be used 146662306a36Sopenharmony_ci * in the next iteration. All vbs are initialized to first 146762306a36Sopenharmony_ci * buffer and we are shifting buffers every iteration, for the 146862306a36Sopenharmony_ci * first two iterations, no buffer will be dequeued. 146962306a36Sopenharmony_ci * This ensures that driver will keep (n-2)th (n-1)th and (n)th 147062306a36Sopenharmony_ci * field when deinterlacing is enabled 147162306a36Sopenharmony_ci */ 147262306a36Sopenharmony_ci if (ctx->src_vbs[2] != ctx->src_vbs[1]) 147362306a36Sopenharmony_ci s_vb = ctx->src_vbs[2]; 147462306a36Sopenharmony_ci else 147562306a36Sopenharmony_ci s_vb = NULL; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (s_vb) 148162306a36Sopenharmony_ci v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (ctx->deinterlacing) { 148862306a36Sopenharmony_ci ctx->src_vbs[2] = ctx->src_vbs[1]; 148962306a36Sopenharmony_ci ctx->src_vbs[1] = ctx->src_vbs[0]; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* 149362306a36Sopenharmony_ci * Since the vb2_buf_done has already been called fir therse 149462306a36Sopenharmony_ci * buffer we can now NULL them out so that we won't try 149562306a36Sopenharmony_ci * to clean out stray pointer later on. 149662306a36Sopenharmony_ci */ 149762306a36Sopenharmony_ci ctx->src_vbs[0] = NULL; 149862306a36Sopenharmony_ci ctx->dst_vb = NULL; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (ctx->aborting) 150162306a36Sopenharmony_ci goto finished; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci ctx->bufs_completed++; 150462306a36Sopenharmony_ci if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) { 150562306a36Sopenharmony_ci device_run(ctx); 150662306a36Sopenharmony_ci goto handled; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cifinished: 151062306a36Sopenharmony_ci vpe_dbg(ctx->dev, "finishing transaction\n"); 151162306a36Sopenharmony_ci ctx->bufs_completed = 0; 151262306a36Sopenharmony_ci v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); 151362306a36Sopenharmony_cihandled: 151462306a36Sopenharmony_ci return IRQ_HANDLED; 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci/* 151862306a36Sopenharmony_ci * video ioctls 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_cistatic int vpe_querycap(struct file *file, void *priv, 152162306a36Sopenharmony_ci struct v4l2_capability *cap) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci strscpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver)); 152462306a36Sopenharmony_ci strscpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card)); 152562306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 152662306a36Sopenharmony_ci VPE_MODULE_NAME); 152762306a36Sopenharmony_ci return 0; 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic int __enum_fmt(struct v4l2_fmtdesc *f, u32 type) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci int i, index; 153362306a36Sopenharmony_ci struct vpe_fmt *fmt = NULL; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci index = 0; 153662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) { 153762306a36Sopenharmony_ci if (vpe_formats[i].types & type) { 153862306a36Sopenharmony_ci if (index == f->index) { 153962306a36Sopenharmony_ci fmt = &vpe_formats[i]; 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci index++; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (!fmt) 154762306a36Sopenharmony_ci return -EINVAL; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 155062306a36Sopenharmony_ci return 0; 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int vpe_enum_fmt(struct file *file, void *priv, 155462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(f->type)) 155762306a36Sopenharmony_ci return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE); 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 156562306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 156662306a36Sopenharmony_ci struct vb2_queue *vq; 156762306a36Sopenharmony_ci struct vpe_q_data *q_data; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 157062306a36Sopenharmony_ci if (!vq) 157162306a36Sopenharmony_ci return -EINVAL; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 157462306a36Sopenharmony_ci if (!q_data) 157562306a36Sopenharmony_ci return -EINVAL; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci *f = q_data->format; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (V4L2_TYPE_IS_CAPTURE(f->type)) { 158062306a36Sopenharmony_ci struct vpe_q_data *s_q_data; 158162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *spix; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* get colorimetry from the source queue */ 158462306a36Sopenharmony_ci s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 158562306a36Sopenharmony_ci spix = &s_q_data->format.fmt.pix_mp; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci pix->colorspace = spix->colorspace; 158862306a36Sopenharmony_ci pix->xfer_func = spix->xfer_func; 158962306a36Sopenharmony_ci pix->ycbcr_enc = spix->ycbcr_enc; 159062306a36Sopenharmony_ci pix->quantization = spix->quantization; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, 159762306a36Sopenharmony_ci struct vpe_fmt *fmt, int type) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 160062306a36Sopenharmony_ci struct v4l2_plane_pix_format *plane_fmt; 160162306a36Sopenharmony_ci unsigned int w_align; 160262306a36Sopenharmony_ci int i, depth, depth_bytes, height; 160362306a36Sopenharmony_ci unsigned int stride = 0; 160462306a36Sopenharmony_ci const struct v4l2_format_info *finfo; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci if (!fmt || !(fmt->types & type)) { 160762306a36Sopenharmony_ci vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", 160862306a36Sopenharmony_ci pix->pixelformat); 160962306a36Sopenharmony_ci fmt = __find_format(V4L2_PIX_FMT_YUYV); 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (pix->field != V4L2_FIELD_NONE && 161362306a36Sopenharmony_ci pix->field != V4L2_FIELD_ALTERNATE && 161462306a36Sopenharmony_ci pix->field != V4L2_FIELD_SEQ_TB && 161562306a36Sopenharmony_ci pix->field != V4L2_FIELD_SEQ_BT) 161662306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci depth = fmt->vpdma_fmt[VPE_LUMA]->depth; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* 162162306a36Sopenharmony_ci * the line stride should 16 byte aligned for VPDMA to work, based on 162262306a36Sopenharmony_ci * the bytes per pixel, figure out how much the width should be aligned 162362306a36Sopenharmony_ci * to make sure line stride is 16 byte aligned 162462306a36Sopenharmony_ci */ 162562306a36Sopenharmony_ci depth_bytes = depth >> 3; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (depth_bytes == 3) { 162862306a36Sopenharmony_ci /* 162962306a36Sopenharmony_ci * if bpp is 3(as in some RGB formats), the pixel width doesn't 163062306a36Sopenharmony_ci * really help in ensuring line stride is 16 byte aligned 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci w_align = 4; 163362306a36Sopenharmony_ci } else { 163462306a36Sopenharmony_ci /* 163562306a36Sopenharmony_ci * for the remainder bpp(4, 2 and 1), the pixel width alignment 163662306a36Sopenharmony_ci * can ensure a line stride alignment of 16 bytes. For example, 163762306a36Sopenharmony_ci * if bpp is 2, then the line stride can be 16 byte aligned if 163862306a36Sopenharmony_ci * the width is 8 byte aligned 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* 164262306a36Sopenharmony_ci * HACK: using order_base_2() here causes lots of asm output 164362306a36Sopenharmony_ci * errors with smatch, on i386: 164462306a36Sopenharmony_ci * ./arch/x86/include/asm/bitops.h:457:22: 164562306a36Sopenharmony_ci * warning: asm output is not an lvalue 164662306a36Sopenharmony_ci * Perhaps some gcc optimization is doing the wrong thing 164762306a36Sopenharmony_ci * there. 164862306a36Sopenharmony_ci * Let's get rid of them by doing the calculus on two steps 164962306a36Sopenharmony_ci */ 165062306a36Sopenharmony_ci w_align = roundup_pow_of_two(VPDMA_DESC_ALIGN / depth_bytes); 165162306a36Sopenharmony_ci w_align = ilog2(w_align); 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, 165562306a36Sopenharmony_ci &pix->height, MIN_H, MAX_H, H_ALIGN, 165662306a36Sopenharmony_ci S_ALIGN); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (!pix->num_planes || pix->num_planes > 2) 165962306a36Sopenharmony_ci pix->num_planes = fmt->coplanar ? 2 : 1; 166062306a36Sopenharmony_ci else if (pix->num_planes > 1 && !fmt->coplanar) 166162306a36Sopenharmony_ci pix->num_planes = 1; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 166462306a36Sopenharmony_ci finfo = v4l2_format_info(fmt->fourcc); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* 166762306a36Sopenharmony_ci * For the actual image parameters, we need to consider the field 166862306a36Sopenharmony_ci * height of the image for SEQ_XX buffers. 166962306a36Sopenharmony_ci */ 167062306a36Sopenharmony_ci if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT) 167162306a36Sopenharmony_ci height = pix->height / 2; 167262306a36Sopenharmony_ci else 167362306a36Sopenharmony_ci height = pix->height; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (!pix->colorspace) { 167662306a36Sopenharmony_ci if (v4l2_is_format_rgb(finfo)) { 167762306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SRGB; 167862306a36Sopenharmony_ci } else { 167962306a36Sopenharmony_ci if (height > 1280) /* HD */ 168062306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_REC709; 168162306a36Sopenharmony_ci else /* SD */ 168262306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SMPTE170M; 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci for (i = 0; i < pix->num_planes; i++) { 168762306a36Sopenharmony_ci plane_fmt = &pix->plane_fmt[i]; 168862306a36Sopenharmony_ci depth = fmt->vpdma_fmt[i]->depth; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci stride = (pix->width * fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; 169162306a36Sopenharmony_ci if (stride > plane_fmt->bytesperline) 169262306a36Sopenharmony_ci plane_fmt->bytesperline = stride; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline, 169562306a36Sopenharmony_ci stride, 169662306a36Sopenharmony_ci VPDMA_MAX_STRIDE); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline, 169962306a36Sopenharmony_ci VPDMA_STRIDE_ALIGN); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci if (i == VPE_LUMA) { 170262306a36Sopenharmony_ci plane_fmt->sizeimage = pix->height * 170362306a36Sopenharmony_ci plane_fmt->bytesperline; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci if (pix->num_planes == 1 && fmt->coplanar) 170662306a36Sopenharmony_ci plane_fmt->sizeimage += pix->height * 170762306a36Sopenharmony_ci plane_fmt->bytesperline * 170862306a36Sopenharmony_ci fmt->vpdma_fmt[VPE_CHROMA]->depth >> 3; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci } else { /* i == VIP_CHROMA */ 171162306a36Sopenharmony_ci plane_fmt->sizeimage = (pix->height * 171262306a36Sopenharmony_ci plane_fmt->bytesperline * 171362306a36Sopenharmony_ci depth) >> 3; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 172362306a36Sopenharmony_ci struct vpe_fmt *fmt = find_format(f); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(f->type)) 172662306a36Sopenharmony_ci return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT); 172762306a36Sopenharmony_ci else 172862306a36Sopenharmony_ci return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE); 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 173462306a36Sopenharmony_ci struct v4l2_pix_format_mplane *qpix; 173562306a36Sopenharmony_ci struct vpe_q_data *q_data; 173662306a36Sopenharmony_ci struct vb2_queue *vq; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 173962306a36Sopenharmony_ci if (!vq) 174062306a36Sopenharmony_ci return -EINVAL; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 174362306a36Sopenharmony_ci vpe_err(ctx->dev, "queue busy\n"); 174462306a36Sopenharmony_ci return -EBUSY; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 174862306a36Sopenharmony_ci if (!q_data) 174962306a36Sopenharmony_ci return -EINVAL; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci qpix = &q_data->format.fmt.pix_mp; 175262306a36Sopenharmony_ci q_data->fmt = find_format(f); 175362306a36Sopenharmony_ci q_data->format = *f; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci q_data->c_rect.left = 0; 175662306a36Sopenharmony_ci q_data->c_rect.top = 0; 175762306a36Sopenharmony_ci q_data->c_rect.width = pix->width; 175862306a36Sopenharmony_ci q_data->c_rect.height = pix->height; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (qpix->field == V4L2_FIELD_ALTERNATE) 176162306a36Sopenharmony_ci q_data->flags |= Q_DATA_INTERLACED_ALTERNATE; 176262306a36Sopenharmony_ci else if (qpix->field == V4L2_FIELD_SEQ_TB) 176362306a36Sopenharmony_ci q_data->flags |= Q_DATA_INTERLACED_SEQ_TB; 176462306a36Sopenharmony_ci else if (qpix->field == V4L2_FIELD_SEQ_BT) 176562306a36Sopenharmony_ci q_data->flags |= Q_DATA_INTERLACED_SEQ_BT; 176662306a36Sopenharmony_ci else 176762306a36Sopenharmony_ci q_data->flags &= ~Q_IS_INTERLACED; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* the crop height is halved for the case of SEQ_XX buffers */ 177062306a36Sopenharmony_ci if (q_data->flags & Q_IS_SEQ_XX) 177162306a36Sopenharmony_ci q_data->c_rect.height /= 2; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", 177462306a36Sopenharmony_ci f->type, pix->width, pix->height, pix->pixelformat, 177562306a36Sopenharmony_ci pix->plane_fmt[0].bytesperline); 177662306a36Sopenharmony_ci if (pix->num_planes == 2) 177762306a36Sopenharmony_ci vpe_dbg(ctx->dev, " bpl_uv %d\n", 177862306a36Sopenharmony_ci pix->plane_fmt[1].bytesperline); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci return 0; 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_cistatic int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) 178462306a36Sopenharmony_ci{ 178562306a36Sopenharmony_ci int ret; 178662306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci ret = vpe_try_fmt(file, priv, f); 178962306a36Sopenharmony_ci if (ret) 179062306a36Sopenharmony_ci return ret; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci ret = __vpe_s_fmt(ctx, f); 179362306a36Sopenharmony_ci if (ret) 179462306a36Sopenharmony_ci return ret; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(f->type)) 179762306a36Sopenharmony_ci set_src_registers(ctx); 179862306a36Sopenharmony_ci else 179962306a36Sopenharmony_ci set_dst_registers(ctx); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci return set_srcdst_params(ctx); 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci struct vpe_q_data *q_data; 180762306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 180862306a36Sopenharmony_ci int height; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 181162306a36Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 181262306a36Sopenharmony_ci return -EINVAL; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci q_data = get_q_data(ctx, s->type); 181562306a36Sopenharmony_ci if (!q_data) 181662306a36Sopenharmony_ci return -EINVAL; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci switch (s->target) { 182162306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 182262306a36Sopenharmony_ci /* 182362306a36Sopenharmony_ci * COMPOSE target is only valid for capture buffer type, return 182462306a36Sopenharmony_ci * error for output buffer type 182562306a36Sopenharmony_ci */ 182662306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 182762306a36Sopenharmony_ci return -EINVAL; 182862306a36Sopenharmony_ci break; 182962306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 183062306a36Sopenharmony_ci /* 183162306a36Sopenharmony_ci * CROP target is only valid for output buffer type, return 183262306a36Sopenharmony_ci * error for capture buffer type 183362306a36Sopenharmony_ci */ 183462306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 183562306a36Sopenharmony_ci return -EINVAL; 183662306a36Sopenharmony_ci break; 183762306a36Sopenharmony_ci /* 183862306a36Sopenharmony_ci * bound and default crop/compose targets are invalid targets to 183962306a36Sopenharmony_ci * try/set 184062306a36Sopenharmony_ci */ 184162306a36Sopenharmony_ci default: 184262306a36Sopenharmony_ci return -EINVAL; 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* 184662306a36Sopenharmony_ci * For SEQ_XX buffers, crop height should be less than the height of 184762306a36Sopenharmony_ci * the field height, not the buffer height 184862306a36Sopenharmony_ci */ 184962306a36Sopenharmony_ci if (q_data->flags & Q_IS_SEQ_XX) 185062306a36Sopenharmony_ci height = pix->height / 2; 185162306a36Sopenharmony_ci else 185262306a36Sopenharmony_ci height = pix->height; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (s->r.top < 0 || s->r.left < 0) { 185562306a36Sopenharmony_ci vpe_err(ctx->dev, "negative values for top and left\n"); 185662306a36Sopenharmony_ci s->r.top = s->r.left = 0; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci v4l_bound_align_image(&s->r.width, MIN_W, pix->width, 1, 186062306a36Sopenharmony_ci &s->r.height, MIN_H, height, H_ALIGN, S_ALIGN); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci /* adjust left/top if cropping rectangle is out of bounds */ 186362306a36Sopenharmony_ci if (s->r.left + s->r.width > pix->width) 186462306a36Sopenharmony_ci s->r.left = pix->width - s->r.width; 186562306a36Sopenharmony_ci if (s->r.top + s->r.height > pix->height) 186662306a36Sopenharmony_ci s->r.top = pix->height - s->r.height; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci return 0; 186962306a36Sopenharmony_ci} 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic int vpe_g_selection(struct file *file, void *fh, 187262306a36Sopenharmony_ci struct v4l2_selection *s) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 187562306a36Sopenharmony_ci struct vpe_q_data *q_data; 187662306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 187762306a36Sopenharmony_ci bool use_c_rect = false; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && 188062306a36Sopenharmony_ci (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) 188162306a36Sopenharmony_ci return -EINVAL; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci q_data = get_q_data(ctx, s->type); 188462306a36Sopenharmony_ci if (!q_data) 188562306a36Sopenharmony_ci return -EINVAL; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci switch (s->target) { 189062306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 189162306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 189262306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 189362306a36Sopenharmony_ci return -EINVAL; 189462306a36Sopenharmony_ci break; 189562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 189662306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 189762306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 189862306a36Sopenharmony_ci return -EINVAL; 189962306a36Sopenharmony_ci break; 190062306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 190162306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 190262306a36Sopenharmony_ci return -EINVAL; 190362306a36Sopenharmony_ci use_c_rect = true; 190462306a36Sopenharmony_ci break; 190562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 190662306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 190762306a36Sopenharmony_ci return -EINVAL; 190862306a36Sopenharmony_ci use_c_rect = true; 190962306a36Sopenharmony_ci break; 191062306a36Sopenharmony_ci default: 191162306a36Sopenharmony_ci return -EINVAL; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci if (use_c_rect) { 191562306a36Sopenharmony_ci /* 191662306a36Sopenharmony_ci * for CROP/COMPOSE target type, return c_rect params from the 191762306a36Sopenharmony_ci * respective buffer type 191862306a36Sopenharmony_ci */ 191962306a36Sopenharmony_ci s->r = q_data->c_rect; 192062306a36Sopenharmony_ci } else { 192162306a36Sopenharmony_ci /* 192262306a36Sopenharmony_ci * for DEFAULT/BOUNDS target type, return width and height from 192362306a36Sopenharmony_ci * S_FMT of the respective buffer type 192462306a36Sopenharmony_ci */ 192562306a36Sopenharmony_ci s->r.left = 0; 192662306a36Sopenharmony_ci s->r.top = 0; 192762306a36Sopenharmony_ci s->r.width = pix->width; 192862306a36Sopenharmony_ci s->r.height = pix->height; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci return 0; 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_cistatic int vpe_s_selection(struct file *file, void *fh, 193662306a36Sopenharmony_ci struct v4l2_selection *s) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 193962306a36Sopenharmony_ci struct vpe_q_data *q_data; 194062306a36Sopenharmony_ci struct v4l2_selection sel = *s; 194162306a36Sopenharmony_ci int ret; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci ret = __vpe_try_selection(ctx, &sel); 194462306a36Sopenharmony_ci if (ret) 194562306a36Sopenharmony_ci return ret; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci q_data = get_q_data(ctx, sel.type); 194862306a36Sopenharmony_ci if (!q_data) 194962306a36Sopenharmony_ci return -EINVAL; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci if ((q_data->c_rect.left == sel.r.left) && 195262306a36Sopenharmony_ci (q_data->c_rect.top == sel.r.top) && 195362306a36Sopenharmony_ci (q_data->c_rect.width == sel.r.width) && 195462306a36Sopenharmony_ci (q_data->c_rect.height == sel.r.height)) { 195562306a36Sopenharmony_ci vpe_dbg(ctx->dev, 195662306a36Sopenharmony_ci "requested crop/compose values are already set\n"); 195762306a36Sopenharmony_ci return 0; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci q_data->c_rect = sel.r; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci return set_srcdst_params(ctx); 196362306a36Sopenharmony_ci} 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci/* 196662306a36Sopenharmony_ci * defines number of buffers/frames a context can process with VPE before 196762306a36Sopenharmony_ci * switching to a different context. default value is 1 buffer per context 196862306a36Sopenharmony_ci */ 196962306a36Sopenharmony_ci#define V4L2_CID_VPE_BUFS_PER_JOB (V4L2_CID_USER_TI_VPE_BASE + 0) 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_cistatic int vpe_s_ctrl(struct v4l2_ctrl *ctrl) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci struct vpe_ctx *ctx = 197462306a36Sopenharmony_ci container_of(ctrl->handler, struct vpe_ctx, hdl); 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci switch (ctrl->id) { 197762306a36Sopenharmony_ci case V4L2_CID_VPE_BUFS_PER_JOB: 197862306a36Sopenharmony_ci ctx->bufs_per_job = ctrl->val; 197962306a36Sopenharmony_ci break; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci default: 198262306a36Sopenharmony_ci vpe_err(ctx->dev, "Invalid control\n"); 198362306a36Sopenharmony_ci return -EINVAL; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci return 0; 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops vpe_ctrl_ops = { 199062306a36Sopenharmony_ci .s_ctrl = vpe_s_ctrl, 199162306a36Sopenharmony_ci}; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops vpe_ioctl_ops = { 199462306a36Sopenharmony_ci .vidioc_querycap = vpe_querycap, 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vpe_enum_fmt, 199762306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, 199862306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, 199962306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = vpe_enum_fmt, 200262306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, 200362306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, 200462306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci .vidioc_g_selection = vpe_g_selection, 200762306a36Sopenharmony_ci .vidioc_s_selection = vpe_s_selection, 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 201062306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 201162306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 201262306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 201362306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 201462306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 201562306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 201862306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 201962306a36Sopenharmony_ci}; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci/* 202262306a36Sopenharmony_ci * Queue operations 202362306a36Sopenharmony_ci */ 202462306a36Sopenharmony_cistatic int vpe_queue_setup(struct vb2_queue *vq, 202562306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 202662306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci int i; 202962306a36Sopenharmony_ci struct vpe_ctx *ctx = vb2_get_drv_priv(vq); 203062306a36Sopenharmony_ci struct vpe_q_data *q_data; 203162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci q_data = get_q_data(ctx, vq->type); 203462306a36Sopenharmony_ci if (!q_data) 203562306a36Sopenharmony_ci return -EINVAL; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 203862306a36Sopenharmony_ci *nplanes = pix->num_planes; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci for (i = 0; i < *nplanes; i++) 204162306a36Sopenharmony_ci sizes[i] = pix->plane_fmt[i].sizeimage; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, 204462306a36Sopenharmony_ci sizes[VPE_LUMA]); 204562306a36Sopenharmony_ci if (*nplanes == 2) 204662306a36Sopenharmony_ci vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci return 0; 204962306a36Sopenharmony_ci} 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_cistatic int vpe_buf_prepare(struct vb2_buffer *vb) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 205462306a36Sopenharmony_ci struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 205562306a36Sopenharmony_ci struct vpe_q_data *q_data; 205662306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 205762306a36Sopenharmony_ci int i; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci q_data = get_q_data(ctx, vb->vb2_queue->type); 206262306a36Sopenharmony_ci if (!q_data) 206362306a36Sopenharmony_ci return -EINVAL; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci pix = &q_data->format.fmt.pix_mp; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 206862306a36Sopenharmony_ci if (!(q_data->flags & Q_IS_INTERLACED)) { 206962306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 207062306a36Sopenharmony_ci } else { 207162306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_TOP && 207262306a36Sopenharmony_ci vbuf->field != V4L2_FIELD_BOTTOM && 207362306a36Sopenharmony_ci vbuf->field != V4L2_FIELD_SEQ_TB && 207462306a36Sopenharmony_ci vbuf->field != V4L2_FIELD_SEQ_BT) 207562306a36Sopenharmony_ci return -EINVAL; 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci for (i = 0; i < pix->num_planes; i++) { 208062306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { 208162306a36Sopenharmony_ci vpe_err(ctx->dev, 208262306a36Sopenharmony_ci "data will not fit into plane (%lu < %lu)\n", 208362306a36Sopenharmony_ci vb2_plane_size(vb, i), 208462306a36Sopenharmony_ci (long)pix->plane_fmt[i].sizeimage); 208562306a36Sopenharmony_ci return -EINVAL; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci for (i = 0; i < pix->num_planes; i++) 209062306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci return 0; 209362306a36Sopenharmony_ci} 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_cistatic void vpe_buf_queue(struct vb2_buffer *vb) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 209862306a36Sopenharmony_ci struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_cistatic int check_srcdst_sizes(struct vpe_ctx *ctx) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; 210662306a36Sopenharmony_ci struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; 210762306a36Sopenharmony_ci unsigned int src_w = s_q_data->c_rect.width; 210862306a36Sopenharmony_ci unsigned int src_h = s_q_data->c_rect.height; 210962306a36Sopenharmony_ci unsigned int dst_w = d_q_data->c_rect.width; 211062306a36Sopenharmony_ci unsigned int dst_h = d_q_data->c_rect.height; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci if (src_w == dst_w && src_h == dst_h) 211362306a36Sopenharmony_ci return 0; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci if (src_h <= SC_MAX_PIXEL_HEIGHT && 211662306a36Sopenharmony_ci src_w <= SC_MAX_PIXEL_WIDTH && 211762306a36Sopenharmony_ci dst_h <= SC_MAX_PIXEL_HEIGHT && 211862306a36Sopenharmony_ci dst_w <= SC_MAX_PIXEL_WIDTH) 211962306a36Sopenharmony_ci return 0; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci return -1; 212262306a36Sopenharmony_ci} 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_cistatic void vpe_return_all_buffers(struct vpe_ctx *ctx, struct vb2_queue *q, 212562306a36Sopenharmony_ci enum vb2_buffer_state state) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vb; 212862306a36Sopenharmony_ci unsigned long flags; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci for (;;) { 213162306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) 213262306a36Sopenharmony_ci vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 213362306a36Sopenharmony_ci else 213462306a36Sopenharmony_ci vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 213562306a36Sopenharmony_ci if (!vb) 213662306a36Sopenharmony_ci break; 213762306a36Sopenharmony_ci spin_lock_irqsave(&ctx->dev->lock, flags); 213862306a36Sopenharmony_ci v4l2_m2m_buf_done(vb, state); 213962306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->dev->lock, flags); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci /* 214362306a36Sopenharmony_ci * Cleanup the in-transit vb2 buffers that have been 214462306a36Sopenharmony_ci * removed from their respective queue already but for 214562306a36Sopenharmony_ci * which procecessing has not been completed yet. 214662306a36Sopenharmony_ci */ 214762306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) { 214862306a36Sopenharmony_ci spin_lock_irqsave(&ctx->dev->lock, flags); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci if (ctx->src_vbs[2]) 215162306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->src_vbs[2], state); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) 215462306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->src_vbs[1], state); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (ctx->src_vbs[0] && 215762306a36Sopenharmony_ci (ctx->src_vbs[0] != ctx->src_vbs[1]) && 215862306a36Sopenharmony_ci (ctx->src_vbs[0] != ctx->src_vbs[2])) 215962306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->src_vbs[0], state); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci ctx->src_vbs[2] = NULL; 216262306a36Sopenharmony_ci ctx->src_vbs[1] = NULL; 216362306a36Sopenharmony_ci ctx->src_vbs[0] = NULL; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->dev->lock, flags); 216662306a36Sopenharmony_ci } else { 216762306a36Sopenharmony_ci if (ctx->dst_vb) { 216862306a36Sopenharmony_ci spin_lock_irqsave(&ctx->dev->lock, flags); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci v4l2_m2m_buf_done(ctx->dst_vb, state); 217162306a36Sopenharmony_ci ctx->dst_vb = NULL; 217262306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->dev->lock, flags); 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci} 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_cistatic int vpe_start_streaming(struct vb2_queue *q, unsigned int count) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci struct vpe_ctx *ctx = vb2_get_drv_priv(q); 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci /* Check any of the size exceed maximum scaling sizes */ 218262306a36Sopenharmony_ci if (check_srcdst_sizes(ctx)) { 218362306a36Sopenharmony_ci vpe_err(ctx->dev, 218462306a36Sopenharmony_ci "Conversion setup failed, check source and destination parameters\n" 218562306a36Sopenharmony_ci ); 218662306a36Sopenharmony_ci vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_QUEUED); 218762306a36Sopenharmony_ci return -EINVAL; 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci if (ctx->deinterlacing) 219162306a36Sopenharmony_ci config_edi_input_mode(ctx, 0x0); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (ctx->sequence != 0) 219462306a36Sopenharmony_ci set_srcdst_params(ctx); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci return 0; 219762306a36Sopenharmony_ci} 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_cistatic void vpe_stop_streaming(struct vb2_queue *q) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci struct vpe_ctx *ctx = vb2_get_drv_priv(q); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci vpe_dump_regs(ctx->dev); 220462306a36Sopenharmony_ci vpdma_dump_regs(ctx->dev->vpdma); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_ERROR); 220762306a36Sopenharmony_ci} 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_cistatic const struct vb2_ops vpe_qops = { 221062306a36Sopenharmony_ci .queue_setup = vpe_queue_setup, 221162306a36Sopenharmony_ci .buf_prepare = vpe_buf_prepare, 221262306a36Sopenharmony_ci .buf_queue = vpe_buf_queue, 221362306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 221462306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 221562306a36Sopenharmony_ci .start_streaming = vpe_start_streaming, 221662306a36Sopenharmony_ci .stop_streaming = vpe_stop_streaming, 221762306a36Sopenharmony_ci}; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 222062306a36Sopenharmony_ci struct vb2_queue *dst_vq) 222162306a36Sopenharmony_ci{ 222262306a36Sopenharmony_ci struct vpe_ctx *ctx = priv; 222362306a36Sopenharmony_ci struct vpe_dev *dev = ctx->dev; 222462306a36Sopenharmony_ci int ret; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci memset(src_vq, 0, sizeof(*src_vq)); 222762306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 222862306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 222962306a36Sopenharmony_ci src_vq->drv_priv = ctx; 223062306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 223162306a36Sopenharmony_ci src_vq->ops = &vpe_qops; 223262306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 223362306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 223462306a36Sopenharmony_ci src_vq->lock = &dev->dev_mutex; 223562306a36Sopenharmony_ci src_vq->dev = dev->v4l2_dev.dev; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 223862306a36Sopenharmony_ci if (ret) 223962306a36Sopenharmony_ci return ret; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci memset(dst_vq, 0, sizeof(*dst_vq)); 224262306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 224362306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 224462306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 224562306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 224662306a36Sopenharmony_ci dst_vq->ops = &vpe_qops; 224762306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 224862306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 224962306a36Sopenharmony_ci dst_vq->lock = &dev->dev_mutex; 225062306a36Sopenharmony_ci dst_vq->dev = dev->v4l2_dev.dev; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cistatic const struct v4l2_ctrl_config vpe_bufs_per_job = { 225662306a36Sopenharmony_ci .ops = &vpe_ctrl_ops, 225762306a36Sopenharmony_ci .id = V4L2_CID_VPE_BUFS_PER_JOB, 225862306a36Sopenharmony_ci .name = "Buffers Per Transaction", 225962306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 226062306a36Sopenharmony_ci .def = VPE_DEF_BUFS_PER_JOB, 226162306a36Sopenharmony_ci .min = 1, 226262306a36Sopenharmony_ci .max = VIDEO_MAX_FRAME, 226362306a36Sopenharmony_ci .step = 1, 226462306a36Sopenharmony_ci}; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci/* 226762306a36Sopenharmony_ci * File operations 226862306a36Sopenharmony_ci */ 226962306a36Sopenharmony_cistatic int vpe_open(struct file *file) 227062306a36Sopenharmony_ci{ 227162306a36Sopenharmony_ci struct vpe_dev *dev = video_drvdata(file); 227262306a36Sopenharmony_ci struct vpe_q_data *s_q_data; 227362306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 227462306a36Sopenharmony_ci struct vpe_ctx *ctx; 227562306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix; 227662306a36Sopenharmony_ci int ret; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci vpe_dbg(dev, "vpe_open\n"); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 228162306a36Sopenharmony_ci if (!ctx) 228262306a36Sopenharmony_ci return -ENOMEM; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci ctx->dev = dev; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->dev_mutex)) { 228762306a36Sopenharmony_ci ret = -ERESTARTSYS; 228862306a36Sopenharmony_ci goto free_ctx; 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE, 229262306a36Sopenharmony_ci VPDMA_LIST_TYPE_NORMAL); 229362306a36Sopenharmony_ci if (ret != 0) 229462306a36Sopenharmony_ci goto unlock; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb)); 229762306a36Sopenharmony_ci if (ret != 0) 229862306a36Sopenharmony_ci goto free_desc_list; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); 230162306a36Sopenharmony_ci if (ret != 0) 230262306a36Sopenharmony_ci goto free_mmr_adb; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); 230562306a36Sopenharmony_ci if (ret != 0) 230662306a36Sopenharmony_ci goto free_sc_h; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci init_adb_hdrs(ctx); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 231162306a36Sopenharmony_ci file->private_data = ctx; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci hdl = &ctx->hdl; 231462306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 1); 231562306a36Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL); 231662306a36Sopenharmony_ci if (hdl->error) { 231762306a36Sopenharmony_ci ret = hdl->error; 231862306a36Sopenharmony_ci goto exit_fh; 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci ctx->fh.ctrl_handler = hdl; 232162306a36Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci s_q_data = &ctx->q_data[Q_DATA_SRC]; 232462306a36Sopenharmony_ci pix = &s_q_data->format.fmt.pix_mp; 232562306a36Sopenharmony_ci s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV); 232662306a36Sopenharmony_ci pix->pixelformat = s_q_data->fmt->fourcc; 232762306a36Sopenharmony_ci s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 232862306a36Sopenharmony_ci pix->width = 1920; 232962306a36Sopenharmony_ci pix->height = 1080; 233062306a36Sopenharmony_ci pix->num_planes = 1; 233162306a36Sopenharmony_ci pix->plane_fmt[VPE_LUMA].bytesperline = (pix->width * 233262306a36Sopenharmony_ci s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; 233362306a36Sopenharmony_ci pix->plane_fmt[VPE_LUMA].sizeimage = 233462306a36Sopenharmony_ci pix->plane_fmt[VPE_LUMA].bytesperline * 233562306a36Sopenharmony_ci pix->height; 233662306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_REC709; 233762306a36Sopenharmony_ci pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; 233862306a36Sopenharmony_ci pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 233962306a36Sopenharmony_ci pix->quantization = V4L2_QUANTIZATION_DEFAULT; 234062306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 234162306a36Sopenharmony_ci s_q_data->c_rect.left = 0; 234262306a36Sopenharmony_ci s_q_data->c_rect.top = 0; 234362306a36Sopenharmony_ci s_q_data->c_rect.width = pix->width; 234462306a36Sopenharmony_ci s_q_data->c_rect.height = pix->height; 234562306a36Sopenharmony_ci s_q_data->flags = 0; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci ctx->q_data[Q_DATA_DST] = *s_q_data; 234862306a36Sopenharmony_ci ctx->q_data[Q_DATA_DST].format.type = 234962306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci set_dei_shadow_registers(ctx); 235262306a36Sopenharmony_ci set_src_registers(ctx); 235362306a36Sopenharmony_ci set_dst_registers(ctx); 235462306a36Sopenharmony_ci ret = set_srcdst_params(ctx); 235562306a36Sopenharmony_ci if (ret) 235662306a36Sopenharmony_ci goto exit_fh; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 236162306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 236262306a36Sopenharmony_ci goto exit_fh; 236362306a36Sopenharmony_ci } 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci /* 236862306a36Sopenharmony_ci * for now, just report the creation of the first instance, we can later 236962306a36Sopenharmony_ci * optimize the driver to enable or disable clocks when the first 237062306a36Sopenharmony_ci * instance is created or the last instance released 237162306a36Sopenharmony_ci */ 237262306a36Sopenharmony_ci if (atomic_inc_return(&dev->num_instances) == 1) 237362306a36Sopenharmony_ci vpe_dbg(dev, "first instance created\n"); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci ctx->load_mmrs = true; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n", 238062306a36Sopenharmony_ci ctx, ctx->fh.m2m_ctx); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return 0; 238562306a36Sopenharmony_ciexit_fh: 238662306a36Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 238762306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 238862306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->sc_coeff_v); 238962306a36Sopenharmony_cifree_sc_h: 239062306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->sc_coeff_h); 239162306a36Sopenharmony_cifree_mmr_adb: 239262306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->mmr_adb); 239362306a36Sopenharmony_cifree_desc_list: 239462306a36Sopenharmony_ci vpdma_free_desc_list(&ctx->desc_list); 239562306a36Sopenharmony_ciunlock: 239662306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 239762306a36Sopenharmony_cifree_ctx: 239862306a36Sopenharmony_ci kfree(ctx); 239962306a36Sopenharmony_ci return ret; 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_cistatic int vpe_release(struct file *file) 240362306a36Sopenharmony_ci{ 240462306a36Sopenharmony_ci struct vpe_dev *dev = video_drvdata(file); 240562306a36Sopenharmony_ci struct vpe_ctx *ctx = file->private_data; 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci vpe_dbg(dev, "releasing instance %p\n", ctx); 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci mutex_lock(&dev->dev_mutex); 241062306a36Sopenharmony_ci free_mv_buffers(ctx); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); 241362306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); 241462306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); 241562306a36Sopenharmony_ci vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci vpdma_free_desc_list(&ctx->desc_list); 241862306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->mmr_adb); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->sc_coeff_v); 242162306a36Sopenharmony_ci vpdma_free_desc_buf(&ctx->sc_coeff_h); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 242462306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 242562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 242662306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci kfree(ctx); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci /* 243162306a36Sopenharmony_ci * for now, just report the release of the last instance, we can later 243262306a36Sopenharmony_ci * optimize the driver to enable or disable clocks when the first 243362306a36Sopenharmony_ci * instance is created or the last instance released 243462306a36Sopenharmony_ci */ 243562306a36Sopenharmony_ci if (atomic_dec_return(&dev->num_instances) == 0) 243662306a36Sopenharmony_ci vpe_dbg(dev, "last instance released\n"); 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci mutex_unlock(&dev->dev_mutex); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci return 0; 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_cistatic const struct v4l2_file_operations vpe_fops = { 244462306a36Sopenharmony_ci .owner = THIS_MODULE, 244562306a36Sopenharmony_ci .open = vpe_open, 244662306a36Sopenharmony_ci .release = vpe_release, 244762306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 244862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 244962306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 245062306a36Sopenharmony_ci}; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cistatic const struct video_device vpe_videodev = { 245362306a36Sopenharmony_ci .name = VPE_MODULE_NAME, 245462306a36Sopenharmony_ci .fops = &vpe_fops, 245562306a36Sopenharmony_ci .ioctl_ops = &vpe_ioctl_ops, 245662306a36Sopenharmony_ci .minor = -1, 245762306a36Sopenharmony_ci .release = video_device_release_empty, 245862306a36Sopenharmony_ci .vfl_dir = VFL_DIR_M2M, 245962306a36Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, 246062306a36Sopenharmony_ci}; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_cistatic const struct v4l2_m2m_ops m2m_ops = { 246362306a36Sopenharmony_ci .device_run = device_run, 246462306a36Sopenharmony_ci .job_ready = job_ready, 246562306a36Sopenharmony_ci .job_abort = job_abort, 246662306a36Sopenharmony_ci}; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_cistatic int vpe_runtime_get(struct platform_device *pdev) 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci int r; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "vpe_runtime_get\n"); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci r = pm_runtime_resume_and_get(&pdev->dev); 247562306a36Sopenharmony_ci WARN_ON(r < 0); 247662306a36Sopenharmony_ci return r; 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic void vpe_runtime_put(struct platform_device *pdev) 248062306a36Sopenharmony_ci{ 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci int r; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "vpe_runtime_put\n"); 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci r = pm_runtime_put_sync(&pdev->dev); 248762306a36Sopenharmony_ci WARN_ON(r < 0 && r != -ENOSYS); 248862306a36Sopenharmony_ci} 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_cistatic void vpe_fw_cb(struct platform_device *pdev) 249162306a36Sopenharmony_ci{ 249262306a36Sopenharmony_ci struct vpe_dev *dev = platform_get_drvdata(pdev); 249362306a36Sopenharmony_ci struct video_device *vfd; 249462306a36Sopenharmony_ci int ret; 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci vfd = &dev->vfd; 249762306a36Sopenharmony_ci *vfd = vpe_videodev; 249862306a36Sopenharmony_ci vfd->lock = &dev->dev_mutex; 249962306a36Sopenharmony_ci vfd->v4l2_dev = &dev->v4l2_dev; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 250262306a36Sopenharmony_ci if (ret) { 250362306a36Sopenharmony_ci vpe_err(dev, "Failed to register video device\n"); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci vpe_set_clock_enable(dev, 0); 250662306a36Sopenharmony_ci vpe_runtime_put(pdev); 250762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 250862306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 250962306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci return; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci video_set_drvdata(vfd, dev); 251562306a36Sopenharmony_ci dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", 251662306a36Sopenharmony_ci vfd->num); 251762306a36Sopenharmony_ci} 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_cistatic int vpe_probe(struct platform_device *pdev) 252062306a36Sopenharmony_ci{ 252162306a36Sopenharmony_ci struct vpe_dev *dev; 252262306a36Sopenharmony_ci int ret, irq, func; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 252562306a36Sopenharmony_ci if (ret) { 252662306a36Sopenharmony_ci dev_err(&pdev->dev, 252762306a36Sopenharmony_ci "32-bit consistent DMA enable failed\n"); 252862306a36Sopenharmony_ci return ret; 252962306a36Sopenharmony_ci } 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 253262306a36Sopenharmony_ci if (!dev) 253362306a36Sopenharmony_ci return -ENOMEM; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci spin_lock_init(&dev->lock); 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 253862306a36Sopenharmony_ci if (ret) 253962306a36Sopenharmony_ci return ret; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci atomic_set(&dev->num_instances, 0); 254262306a36Sopenharmony_ci mutex_init(&dev->dev_mutex); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 254562306a36Sopenharmony_ci "vpe_top"); 254662306a36Sopenharmony_ci if (!dev->res) { 254762306a36Sopenharmony_ci dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); 254862306a36Sopenharmony_ci return -ENODEV; 254962306a36Sopenharmony_ci } 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci /* 255262306a36Sopenharmony_ci * HACK: we get resource info from device tree in the form of a list of 255362306a36Sopenharmony_ci * VPE sub blocks, the driver currently uses only the base of vpe_top 255462306a36Sopenharmony_ci * for register access, the driver should be changed later to access 255562306a36Sopenharmony_ci * registers based on the sub block base addresses 255662306a36Sopenharmony_ci */ 255762306a36Sopenharmony_ci dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); 255862306a36Sopenharmony_ci if (!dev->base) { 255962306a36Sopenharmony_ci ret = -ENOMEM; 256062306a36Sopenharmony_ci goto v4l2_dev_unreg; 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 256462306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME, 256562306a36Sopenharmony_ci dev); 256662306a36Sopenharmony_ci if (ret) 256762306a36Sopenharmony_ci goto v4l2_dev_unreg; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci dev->m2m_dev = v4l2_m2m_init(&m2m_ops); 257262306a36Sopenharmony_ci if (IS_ERR(dev->m2m_dev)) { 257362306a36Sopenharmony_ci vpe_err(dev, "Failed to init mem2mem device\n"); 257462306a36Sopenharmony_ci ret = PTR_ERR(dev->m2m_dev); 257562306a36Sopenharmony_ci goto v4l2_dev_unreg; 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci ret = vpe_runtime_get(pdev); 258162306a36Sopenharmony_ci if (ret < 0) 258262306a36Sopenharmony_ci goto rel_m2m; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci /* Perform clk enable followed by reset */ 258562306a36Sopenharmony_ci vpe_set_clock_enable(dev, 1); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci vpe_top_reset(dev); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK, 259062306a36Sopenharmony_ci VPE_PID_FUNC_SHIFT); 259162306a36Sopenharmony_ci vpe_dbg(dev, "VPE PID function %x\n", func); 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci vpe_top_vpdma_reset(dev); 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci dev->sc = sc_create(pdev, "sc"); 259662306a36Sopenharmony_ci if (IS_ERR(dev->sc)) { 259762306a36Sopenharmony_ci ret = PTR_ERR(dev->sc); 259862306a36Sopenharmony_ci goto runtime_put; 259962306a36Sopenharmony_ci } 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci dev->csc = csc_create(pdev, "csc"); 260262306a36Sopenharmony_ci if (IS_ERR(dev->csc)) { 260362306a36Sopenharmony_ci ret = PTR_ERR(dev->csc); 260462306a36Sopenharmony_ci goto runtime_put; 260562306a36Sopenharmony_ci } 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci dev->vpdma = &dev->vpdma_data; 260862306a36Sopenharmony_ci ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb); 260962306a36Sopenharmony_ci if (ret) 261062306a36Sopenharmony_ci goto runtime_put; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci return 0; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ciruntime_put: 261562306a36Sopenharmony_ci vpe_runtime_put(pdev); 261662306a36Sopenharmony_cirel_m2m: 261762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 261862306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 261962306a36Sopenharmony_civ4l2_dev_unreg: 262062306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci return ret; 262362306a36Sopenharmony_ci} 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_cistatic void vpe_remove(struct platform_device *pdev) 262662306a36Sopenharmony_ci{ 262762306a36Sopenharmony_ci struct vpe_dev *dev = platform_get_drvdata(pdev); 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 263262306a36Sopenharmony_ci video_unregister_device(&dev->vfd); 263362306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci vpe_set_clock_enable(dev, 0); 263662306a36Sopenharmony_ci vpe_runtime_put(pdev); 263762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 263862306a36Sopenharmony_ci} 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci#if defined(CONFIG_OF) 264162306a36Sopenharmony_cistatic const struct of_device_id vpe_of_match[] = { 264262306a36Sopenharmony_ci { 264362306a36Sopenharmony_ci .compatible = "ti,dra7-vpe", 264462306a36Sopenharmony_ci }, 264562306a36Sopenharmony_ci {}, 264662306a36Sopenharmony_ci}; 264762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vpe_of_match); 264862306a36Sopenharmony_ci#endif 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_cistatic struct platform_driver vpe_pdrv = { 265162306a36Sopenharmony_ci .probe = vpe_probe, 265262306a36Sopenharmony_ci .remove_new = vpe_remove, 265362306a36Sopenharmony_ci .driver = { 265462306a36Sopenharmony_ci .name = VPE_MODULE_NAME, 265562306a36Sopenharmony_ci .of_match_table = of_match_ptr(vpe_of_match), 265662306a36Sopenharmony_ci }, 265762306a36Sopenharmony_ci}; 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cimodule_platform_driver(vpe_pdrv); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ciMODULE_DESCRIPTION("TI VPE driver"); 266262306a36Sopenharmony_ciMODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>"); 266362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2664