162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Renesas R-Car Fine Display Processor 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Video format converter and frame deinterlacer device. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Kieran Bingham, <kieran@bingham.xyz> 862306a36Sopenharmony_ci * Copyright (c) 2016 Renesas Electronics Corporation. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This code is developed and inspired from the vim2m, rcar_jpu, 1162306a36Sopenharmony_ci * m2m-deinterlace, and vsp1 drivers. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2362306a36Sopenharmony_ci#include <linux/sched.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/timer.h> 2662306a36Sopenharmony_ci#include <media/rcar-fcp.h> 2762306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2862306a36Sopenharmony_ci#include <media/v4l2-device.h> 2962306a36Sopenharmony_ci#include <media/v4l2-event.h> 3062306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3162306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 3262306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned int debug; 3562306a36Sopenharmony_cimodule_param(debug, uint, 0644); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "activate debug info"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Minimum and maximum frame width/height */ 3962306a36Sopenharmony_ci#define FDP1_MIN_W 80U 4062306a36Sopenharmony_ci#define FDP1_MIN_H 80U 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define FDP1_MAX_W 3840U 4362306a36Sopenharmony_ci#define FDP1_MAX_H 2160U 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define FDP1_MAX_PLANES 3U 4662306a36Sopenharmony_ci#define FDP1_MAX_STRIDE 8190U 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Flags that indicate a format can be used for capture/output */ 4962306a36Sopenharmony_ci#define FDP1_CAPTURE BIT(0) 5062306a36Sopenharmony_ci#define FDP1_OUTPUT BIT(1) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define DRIVER_NAME "rcar_fdp1" 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Number of Job's to have available on the processing queue */ 5562306a36Sopenharmony_ci#define FDP1_NUMBER_JOBS 8 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define dprintk(fdp1, fmt, arg...) \ 5862306a36Sopenharmony_ci v4l2_dbg(1, debug, &fdp1->v4l2_dev, "%s: " fmt, __func__, ## arg) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * FDP1 registers and bits 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* FDP1 start register - Imm */ 6562306a36Sopenharmony_ci#define FD1_CTL_CMD 0x0000 6662306a36Sopenharmony_ci#define FD1_CTL_CMD_STRCMD BIT(0) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Sync generator register - Imm */ 6962306a36Sopenharmony_ci#define FD1_CTL_SGCMD 0x0004 7062306a36Sopenharmony_ci#define FD1_CTL_SGCMD_SGEN BIT(0) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Register set end register - Imm */ 7362306a36Sopenharmony_ci#define FD1_CTL_REGEND 0x0008 7462306a36Sopenharmony_ci#define FD1_CTL_REGEND_REGEND BIT(0) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Channel activation register - Vupdt */ 7762306a36Sopenharmony_ci#define FD1_CTL_CHACT 0x000c 7862306a36Sopenharmony_ci#define FD1_CTL_CHACT_SMW BIT(9) 7962306a36Sopenharmony_ci#define FD1_CTL_CHACT_WR BIT(8) 8062306a36Sopenharmony_ci#define FD1_CTL_CHACT_SMR BIT(3) 8162306a36Sopenharmony_ci#define FD1_CTL_CHACT_RD2 BIT(2) 8262306a36Sopenharmony_ci#define FD1_CTL_CHACT_RD1 BIT(1) 8362306a36Sopenharmony_ci#define FD1_CTL_CHACT_RD0 BIT(0) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Operation Mode Register - Vupdt */ 8662306a36Sopenharmony_ci#define FD1_CTL_OPMODE 0x0010 8762306a36Sopenharmony_ci#define FD1_CTL_OPMODE_PRG BIT(4) 8862306a36Sopenharmony_ci#define FD1_CTL_OPMODE_VIMD_INTERRUPT (0 << 0) 8962306a36Sopenharmony_ci#define FD1_CTL_OPMODE_VIMD_BESTEFFORT (1 << 0) 9062306a36Sopenharmony_ci#define FD1_CTL_OPMODE_VIMD_NOINTERRUPT (2 << 0) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define FD1_CTL_VPERIOD 0x0014 9362306a36Sopenharmony_ci#define FD1_CTL_CLKCTRL 0x0018 9462306a36Sopenharmony_ci#define FD1_CTL_CLKCTRL_CSTP_N BIT(0) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Software reset register */ 9762306a36Sopenharmony_ci#define FD1_CTL_SRESET 0x001c 9862306a36Sopenharmony_ci#define FD1_CTL_SRESET_SRST BIT(0) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* Control status register (V-update-status) */ 10162306a36Sopenharmony_ci#define FD1_CTL_STATUS 0x0024 10262306a36Sopenharmony_ci#define FD1_CTL_STATUS_VINT_CNT_MASK GENMASK(31, 16) 10362306a36Sopenharmony_ci#define FD1_CTL_STATUS_VINT_CNT_SHIFT 16 10462306a36Sopenharmony_ci#define FD1_CTL_STATUS_SGREGSET BIT(10) 10562306a36Sopenharmony_ci#define FD1_CTL_STATUS_SGVERR BIT(9) 10662306a36Sopenharmony_ci#define FD1_CTL_STATUS_SGFREND BIT(8) 10762306a36Sopenharmony_ci#define FD1_CTL_STATUS_BSY BIT(0) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define FD1_CTL_VCYCLE_STAT 0x0028 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Interrupt enable register */ 11262306a36Sopenharmony_ci#define FD1_CTL_IRQENB 0x0038 11362306a36Sopenharmony_ci/* Interrupt status register */ 11462306a36Sopenharmony_ci#define FD1_CTL_IRQSTA 0x003c 11562306a36Sopenharmony_ci/* Interrupt control register */ 11662306a36Sopenharmony_ci#define FD1_CTL_IRQFSET 0x0040 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Common IRQ Bit settings */ 11962306a36Sopenharmony_ci#define FD1_CTL_IRQ_VERE BIT(16) 12062306a36Sopenharmony_ci#define FD1_CTL_IRQ_VINTE BIT(4) 12162306a36Sopenharmony_ci#define FD1_CTL_IRQ_FREE BIT(0) 12262306a36Sopenharmony_ci#define FD1_CTL_IRQ_MASK (FD1_CTL_IRQ_VERE | \ 12362306a36Sopenharmony_ci FD1_CTL_IRQ_VINTE | \ 12462306a36Sopenharmony_ci FD1_CTL_IRQ_FREE) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* RPF */ 12762306a36Sopenharmony_ci#define FD1_RPF_SIZE 0x0060 12862306a36Sopenharmony_ci#define FD1_RPF_SIZE_MASK GENMASK(12, 0) 12962306a36Sopenharmony_ci#define FD1_RPF_SIZE_H_SHIFT 16 13062306a36Sopenharmony_ci#define FD1_RPF_SIZE_V_SHIFT 0 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define FD1_RPF_FORMAT 0x0064 13362306a36Sopenharmony_ci#define FD1_RPF_FORMAT_CIPM BIT(16) 13462306a36Sopenharmony_ci#define FD1_RPF_FORMAT_RSPYCS BIT(13) 13562306a36Sopenharmony_ci#define FD1_RPF_FORMAT_RSPUVS BIT(12) 13662306a36Sopenharmony_ci#define FD1_RPF_FORMAT_CF BIT(8) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define FD1_RPF_PSTRIDE 0x0068 13962306a36Sopenharmony_ci#define FD1_RPF_PSTRIDE_Y_SHIFT 16 14062306a36Sopenharmony_ci#define FD1_RPF_PSTRIDE_C_SHIFT 0 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* RPF0 Source Component Y Address register */ 14362306a36Sopenharmony_ci#define FD1_RPF0_ADDR_Y 0x006c 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* RPF1 Current Picture Registers */ 14662306a36Sopenharmony_ci#define FD1_RPF1_ADDR_Y 0x0078 14762306a36Sopenharmony_ci#define FD1_RPF1_ADDR_C0 0x007c 14862306a36Sopenharmony_ci#define FD1_RPF1_ADDR_C1 0x0080 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* RPF2 next picture register */ 15162306a36Sopenharmony_ci#define FD1_RPF2_ADDR_Y 0x0084 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define FD1_RPF_SMSK_ADDR 0x0090 15462306a36Sopenharmony_ci#define FD1_RPF_SWAP 0x0094 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* WPF */ 15762306a36Sopenharmony_ci#define FD1_WPF_FORMAT 0x00c0 15862306a36Sopenharmony_ci#define FD1_WPF_FORMAT_PDV_SHIFT 24 15962306a36Sopenharmony_ci#define FD1_WPF_FORMAT_FCNL BIT(20) 16062306a36Sopenharmony_ci#define FD1_WPF_FORMAT_WSPYCS BIT(15) 16162306a36Sopenharmony_ci#define FD1_WPF_FORMAT_WSPUVS BIT(14) 16262306a36Sopenharmony_ci#define FD1_WPF_FORMAT_WRTM_601_16 (0 << 9) 16362306a36Sopenharmony_ci#define FD1_WPF_FORMAT_WRTM_601_0 (1 << 9) 16462306a36Sopenharmony_ci#define FD1_WPF_FORMAT_WRTM_709_16 (2 << 9) 16562306a36Sopenharmony_ci#define FD1_WPF_FORMAT_CSC BIT(8) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define FD1_WPF_RNDCTL 0x00c4 16862306a36Sopenharmony_ci#define FD1_WPF_RNDCTL_CBRM BIT(28) 16962306a36Sopenharmony_ci#define FD1_WPF_RNDCTL_CLMD_NOCLIP (0 << 12) 17062306a36Sopenharmony_ci#define FD1_WPF_RNDCTL_CLMD_CLIP_16_235 (1 << 12) 17162306a36Sopenharmony_ci#define FD1_WPF_RNDCTL_CLMD_CLIP_1_254 (2 << 12) 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define FD1_WPF_PSTRIDE 0x00c8 17462306a36Sopenharmony_ci#define FD1_WPF_PSTRIDE_Y_SHIFT 16 17562306a36Sopenharmony_ci#define FD1_WPF_PSTRIDE_C_SHIFT 0 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* WPF Destination picture */ 17862306a36Sopenharmony_ci#define FD1_WPF_ADDR_Y 0x00cc 17962306a36Sopenharmony_ci#define FD1_WPF_ADDR_C0 0x00d0 18062306a36Sopenharmony_ci#define FD1_WPF_ADDR_C1 0x00d4 18162306a36Sopenharmony_ci#define FD1_WPF_SWAP 0x00d8 18262306a36Sopenharmony_ci#define FD1_WPF_SWAP_OSWAP_SHIFT 0 18362306a36Sopenharmony_ci#define FD1_WPF_SWAP_SSWAP_SHIFT 4 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* WPF/RPF Common */ 18662306a36Sopenharmony_ci#define FD1_RWPF_SWAP_BYTE BIT(0) 18762306a36Sopenharmony_ci#define FD1_RWPF_SWAP_WORD BIT(1) 18862306a36Sopenharmony_ci#define FD1_RWPF_SWAP_LWRD BIT(2) 18962306a36Sopenharmony_ci#define FD1_RWPF_SWAP_LLWD BIT(3) 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* IPC */ 19262306a36Sopenharmony_ci#define FD1_IPC_MODE 0x0100 19362306a36Sopenharmony_ci#define FD1_IPC_MODE_DLI BIT(8) 19462306a36Sopenharmony_ci#define FD1_IPC_MODE_DIM_ADAPT2D3D (0 << 0) 19562306a36Sopenharmony_ci#define FD1_IPC_MODE_DIM_FIXED2D (1 << 0) 19662306a36Sopenharmony_ci#define FD1_IPC_MODE_DIM_FIXED3D (2 << 0) 19762306a36Sopenharmony_ci#define FD1_IPC_MODE_DIM_PREVFIELD (3 << 0) 19862306a36Sopenharmony_ci#define FD1_IPC_MODE_DIM_NEXTFIELD (4 << 0) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#define FD1_IPC_SMSK_THRESH 0x0104 20162306a36Sopenharmony_ci#define FD1_IPC_SMSK_THRESH_CONST 0x00010002 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci#define FD1_IPC_COMB_DET 0x0108 20462306a36Sopenharmony_ci#define FD1_IPC_COMB_DET_CONST 0x00200040 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci#define FD1_IPC_MOTDEC 0x010c 20762306a36Sopenharmony_ci#define FD1_IPC_MOTDEC_CONST 0x00008020 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* DLI registers */ 21062306a36Sopenharmony_ci#define FD1_IPC_DLI_BLEND 0x0120 21162306a36Sopenharmony_ci#define FD1_IPC_DLI_BLEND_CONST 0x0080ff02 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#define FD1_IPC_DLI_HGAIN 0x0124 21462306a36Sopenharmony_ci#define FD1_IPC_DLI_HGAIN_CONST 0x001000ff 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define FD1_IPC_DLI_SPRS 0x0128 21762306a36Sopenharmony_ci#define FD1_IPC_DLI_SPRS_CONST 0x009004ff 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#define FD1_IPC_DLI_ANGLE 0x012c 22062306a36Sopenharmony_ci#define FD1_IPC_DLI_ANGLE_CONST 0x0004080c 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define FD1_IPC_DLI_ISOPIX0 0x0130 22362306a36Sopenharmony_ci#define FD1_IPC_DLI_ISOPIX0_CONST 0xff10ff10 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci#define FD1_IPC_DLI_ISOPIX1 0x0134 22662306a36Sopenharmony_ci#define FD1_IPC_DLI_ISOPIX1_CONST 0x0000ff10 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* Sensor registers */ 22962306a36Sopenharmony_ci#define FD1_IPC_SENSOR_TH0 0x0140 23062306a36Sopenharmony_ci#define FD1_IPC_SENSOR_TH0_CONST 0x20208080 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#define FD1_IPC_SENSOR_TH1 0x0144 23362306a36Sopenharmony_ci#define FD1_IPC_SENSOR_TH1_CONST 0 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL0 0x0170 23662306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL0_CONST 0x00002201 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL1 0x0174 23962306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL1_CONST 0 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL2 0x0178 24262306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL2_X_SHIFT 16 24362306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL2_Y_SHIFT 0 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL3 0x017c 24662306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL3_0_SHIFT 16 24762306a36Sopenharmony_ci#define FD1_IPC_SENSOR_CTL3_1_SHIFT 0 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* Line memory pixel number register */ 25062306a36Sopenharmony_ci#define FD1_IPC_LMEM 0x01e0 25162306a36Sopenharmony_ci#define FD1_IPC_LMEM_LINEAR 1024 25262306a36Sopenharmony_ci#define FD1_IPC_LMEM_TILE 960 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* Internal Data (HW Version) */ 25562306a36Sopenharmony_ci#define FD1_IP_INTDATA 0x0800 25662306a36Sopenharmony_ci/* R-Car Gen2 HW manual says zero, but actual value matches R-Car H3 ES1.x */ 25762306a36Sopenharmony_ci#define FD1_IP_GEN2 0x02010101 25862306a36Sopenharmony_ci#define FD1_IP_M3W 0x02010202 25962306a36Sopenharmony_ci#define FD1_IP_H3 0x02010203 26062306a36Sopenharmony_ci#define FD1_IP_M3N 0x02010204 26162306a36Sopenharmony_ci#define FD1_IP_E3 0x02010205 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* LUTs */ 26462306a36Sopenharmony_ci#define FD1_LUT_DIF_ADJ 0x1000 26562306a36Sopenharmony_ci#define FD1_LUT_SAD_ADJ 0x1400 26662306a36Sopenharmony_ci#define FD1_LUT_BLD_GAIN 0x1800 26762306a36Sopenharmony_ci#define FD1_LUT_DIF_GAIN 0x1c00 26862306a36Sopenharmony_ci#define FD1_LUT_MDET 0x2000 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/** 27162306a36Sopenharmony_ci * struct fdp1_fmt - The FDP1 internal format data 27262306a36Sopenharmony_ci * @fourcc: the fourcc code, to match the V4L2 API 27362306a36Sopenharmony_ci * @bpp: bits per pixel per plane 27462306a36Sopenharmony_ci * @num_planes: number of planes 27562306a36Sopenharmony_ci * @hsub: horizontal subsampling factor 27662306a36Sopenharmony_ci * @vsub: vertical subsampling factor 27762306a36Sopenharmony_ci * @fmt: 7-bit format code for the fdp1 hardware 27862306a36Sopenharmony_ci * @swap_yc: the Y and C components are swapped (Y comes before C) 27962306a36Sopenharmony_ci * @swap_uv: the U and V components are swapped (V comes before U) 28062306a36Sopenharmony_ci * @swap: swap register control 28162306a36Sopenharmony_ci * @types: types of queue this format is applicable to 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistruct fdp1_fmt { 28462306a36Sopenharmony_ci u32 fourcc; 28562306a36Sopenharmony_ci u8 bpp[3]; 28662306a36Sopenharmony_ci u8 num_planes; 28762306a36Sopenharmony_ci u8 hsub; 28862306a36Sopenharmony_ci u8 vsub; 28962306a36Sopenharmony_ci u8 fmt; 29062306a36Sopenharmony_ci bool swap_yc; 29162306a36Sopenharmony_ci bool swap_uv; 29262306a36Sopenharmony_ci u8 swap; 29362306a36Sopenharmony_ci u8 types; 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic const struct fdp1_fmt fdp1_formats[] = { 29762306a36Sopenharmony_ci /* RGB formats are only supported by the Write Pixel Formatter */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci { V4L2_PIX_FMT_RGB332, { 8, 0, 0 }, 1, 1, 1, 0x00, false, false, 30062306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 30162306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 30262306a36Sopenharmony_ci FDP1_CAPTURE }, 30362306a36Sopenharmony_ci { V4L2_PIX_FMT_XRGB444, { 16, 0, 0 }, 1, 1, 1, 0x01, false, false, 30462306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 30562306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD, 30662306a36Sopenharmony_ci FDP1_CAPTURE }, 30762306a36Sopenharmony_ci { V4L2_PIX_FMT_XRGB555, { 16, 0, 0 }, 1, 1, 1, 0x04, false, false, 30862306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 30962306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD, 31062306a36Sopenharmony_ci FDP1_CAPTURE }, 31162306a36Sopenharmony_ci { V4L2_PIX_FMT_RGB565, { 16, 0, 0 }, 1, 1, 1, 0x06, false, false, 31262306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 31362306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD, 31462306a36Sopenharmony_ci FDP1_CAPTURE }, 31562306a36Sopenharmony_ci { V4L2_PIX_FMT_ABGR32, { 32, 0, 0 }, 1, 1, 1, 0x13, false, false, 31662306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD, 31762306a36Sopenharmony_ci FDP1_CAPTURE }, 31862306a36Sopenharmony_ci { V4L2_PIX_FMT_XBGR32, { 32, 0, 0 }, 1, 1, 1, 0x13, false, false, 31962306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD, 32062306a36Sopenharmony_ci FDP1_CAPTURE }, 32162306a36Sopenharmony_ci { V4L2_PIX_FMT_ARGB32, { 32, 0, 0 }, 1, 1, 1, 0x13, false, false, 32262306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 32362306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 32462306a36Sopenharmony_ci FDP1_CAPTURE }, 32562306a36Sopenharmony_ci { V4L2_PIX_FMT_XRGB32, { 32, 0, 0 }, 1, 1, 1, 0x13, false, false, 32662306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 32762306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 32862306a36Sopenharmony_ci FDP1_CAPTURE }, 32962306a36Sopenharmony_ci { V4L2_PIX_FMT_RGB24, { 24, 0, 0 }, 1, 1, 1, 0x15, false, false, 33062306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 33162306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 33262306a36Sopenharmony_ci FDP1_CAPTURE }, 33362306a36Sopenharmony_ci { V4L2_PIX_FMT_BGR24, { 24, 0, 0 }, 1, 1, 1, 0x18, false, false, 33462306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 33562306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 33662306a36Sopenharmony_ci FDP1_CAPTURE }, 33762306a36Sopenharmony_ci { V4L2_PIX_FMT_ARGB444, { 16, 0, 0 }, 1, 1, 1, 0x19, false, false, 33862306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 33962306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD, 34062306a36Sopenharmony_ci FDP1_CAPTURE }, 34162306a36Sopenharmony_ci { V4L2_PIX_FMT_ARGB555, { 16, 0, 0 }, 1, 1, 1, 0x1b, false, false, 34262306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 34362306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD, 34462306a36Sopenharmony_ci FDP1_CAPTURE }, 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* YUV Formats are supported by Read and Write Pixel Formatters */ 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci { V4L2_PIX_FMT_NV16M, { 8, 16, 0 }, 2, 2, 1, 0x41, false, false, 34962306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 35062306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 35162306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 35262306a36Sopenharmony_ci { V4L2_PIX_FMT_NV61M, { 8, 16, 0 }, 2, 2, 1, 0x41, false, true, 35362306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 35462306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 35562306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 35662306a36Sopenharmony_ci { V4L2_PIX_FMT_NV12M, { 8, 16, 0 }, 2, 2, 2, 0x42, false, false, 35762306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 35862306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 35962306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 36062306a36Sopenharmony_ci { V4L2_PIX_FMT_NV21M, { 8, 16, 0 }, 2, 2, 2, 0x42, false, true, 36162306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 36262306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 36362306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 36462306a36Sopenharmony_ci { V4L2_PIX_FMT_UYVY, { 16, 0, 0 }, 1, 2, 1, 0x47, false, false, 36562306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 36662306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 36762306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 36862306a36Sopenharmony_ci { V4L2_PIX_FMT_VYUY, { 16, 0, 0 }, 1, 2, 1, 0x47, false, true, 36962306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 37062306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 37162306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 37262306a36Sopenharmony_ci { V4L2_PIX_FMT_YUYV, { 16, 0, 0 }, 1, 2, 1, 0x47, true, false, 37362306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 37462306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 37562306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 37662306a36Sopenharmony_ci { V4L2_PIX_FMT_YVYU, { 16, 0, 0 }, 1, 2, 1, 0x47, true, true, 37762306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 37862306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 37962306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 38062306a36Sopenharmony_ci { V4L2_PIX_FMT_YUV444M, { 8, 8, 8 }, 3, 1, 1, 0x4a, false, false, 38162306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 38262306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 38362306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 38462306a36Sopenharmony_ci { V4L2_PIX_FMT_YVU444M, { 8, 8, 8 }, 3, 1, 1, 0x4a, false, true, 38562306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 38662306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 38762306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 38862306a36Sopenharmony_ci { V4L2_PIX_FMT_YUV422M, { 8, 8, 8 }, 3, 2, 1, 0x4b, false, false, 38962306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 39062306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 39162306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 39262306a36Sopenharmony_ci { V4L2_PIX_FMT_YVU422M, { 8, 8, 8 }, 3, 2, 1, 0x4b, false, true, 39362306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 39462306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 39562306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 39662306a36Sopenharmony_ci { V4L2_PIX_FMT_YUV420M, { 8, 8, 8 }, 3, 2, 2, 0x4c, false, false, 39762306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 39862306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 39962306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 40062306a36Sopenharmony_ci { V4L2_PIX_FMT_YVU420M, { 8, 8, 8 }, 3, 2, 2, 0x4c, false, true, 40162306a36Sopenharmony_ci FD1_RWPF_SWAP_LLWD | FD1_RWPF_SWAP_LWRD | 40262306a36Sopenharmony_ci FD1_RWPF_SWAP_WORD | FD1_RWPF_SWAP_BYTE, 40362306a36Sopenharmony_ci FDP1_CAPTURE | FDP1_OUTPUT }, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int fdp1_fmt_is_rgb(const struct fdp1_fmt *fmt) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci return fmt->fmt <= 0x1b; /* Last RGB code */ 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * FDP1 Lookup tables range from 0...255 only 41362306a36Sopenharmony_ci * 41462306a36Sopenharmony_ci * Each table must be less than 256 entries, and all tables 41562306a36Sopenharmony_ci * are padded out to 256 entries by duplicating the last value. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_cistatic const u8 fdp1_diff_adj[] = { 41862306a36Sopenharmony_ci 0x00, 0x24, 0x43, 0x5e, 0x76, 0x8c, 0x9e, 0xaf, 41962306a36Sopenharmony_ci 0xbd, 0xc9, 0xd4, 0xdd, 0xe4, 0xea, 0xef, 0xf3, 42062306a36Sopenharmony_ci 0xf6, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic const u8 fdp1_sad_adj[] = { 42462306a36Sopenharmony_ci 0x00, 0x24, 0x43, 0x5e, 0x76, 0x8c, 0x9e, 0xaf, 42562306a36Sopenharmony_ci 0xbd, 0xc9, 0xd4, 0xdd, 0xe4, 0xea, 0xef, 0xf3, 42662306a36Sopenharmony_ci 0xf6, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic const u8 fdp1_bld_gain[] = { 43062306a36Sopenharmony_ci 0x80, 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic const u8 fdp1_dif_gain[] = { 43462306a36Sopenharmony_ci 0x80, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const u8 fdp1_mdet[] = { 43862306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 43962306a36Sopenharmony_ci 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 44062306a36Sopenharmony_ci 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 44162306a36Sopenharmony_ci 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 44262306a36Sopenharmony_ci 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 44362306a36Sopenharmony_ci 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 44462306a36Sopenharmony_ci 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 44562306a36Sopenharmony_ci 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 44662306a36Sopenharmony_ci 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 44762306a36Sopenharmony_ci 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 44862306a36Sopenharmony_ci 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 44962306a36Sopenharmony_ci 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 45062306a36Sopenharmony_ci 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 45162306a36Sopenharmony_ci 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 45262306a36Sopenharmony_ci 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 45362306a36Sopenharmony_ci 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 45462306a36Sopenharmony_ci 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 45562306a36Sopenharmony_ci 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 45662306a36Sopenharmony_ci 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 45762306a36Sopenharmony_ci 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 45862306a36Sopenharmony_ci 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 45962306a36Sopenharmony_ci 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 46062306a36Sopenharmony_ci 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 46162306a36Sopenharmony_ci 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 46262306a36Sopenharmony_ci 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 46362306a36Sopenharmony_ci 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 46462306a36Sopenharmony_ci 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 46562306a36Sopenharmony_ci 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 46662306a36Sopenharmony_ci 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 46762306a36Sopenharmony_ci 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 46862306a36Sopenharmony_ci 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 46962306a36Sopenharmony_ci 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* Per-queue, driver-specific private data */ 47362306a36Sopenharmony_cistruct fdp1_q_data { 47462306a36Sopenharmony_ci const struct fdp1_fmt *fmt; 47562306a36Sopenharmony_ci struct v4l2_pix_format_mplane format; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci unsigned int vsize; 47862306a36Sopenharmony_ci unsigned int stride_y; 47962306a36Sopenharmony_ci unsigned int stride_c; 48062306a36Sopenharmony_ci}; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const struct fdp1_fmt *fdp1_find_format(u32 pixelformat) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci const struct fdp1_fmt *fmt; 48562306a36Sopenharmony_ci unsigned int i; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fdp1_formats); i++) { 48862306a36Sopenharmony_ci fmt = &fdp1_formats[i]; 48962306a36Sopenharmony_ci if (fmt->fourcc == pixelformat) 49062306a36Sopenharmony_ci return fmt; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return NULL; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cienum fdp1_deint_mode { 49762306a36Sopenharmony_ci FDP1_PROGRESSIVE = 0, /* Must be zero when !deinterlacing */ 49862306a36Sopenharmony_ci FDP1_ADAPT2D3D, 49962306a36Sopenharmony_ci FDP1_FIXED2D, 50062306a36Sopenharmony_ci FDP1_FIXED3D, 50162306a36Sopenharmony_ci FDP1_PREVFIELD, 50262306a36Sopenharmony_ci FDP1_NEXTFIELD, 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci#define FDP1_DEINT_MODE_USES_NEXT(mode) \ 50662306a36Sopenharmony_ci (mode == FDP1_ADAPT2D3D || \ 50762306a36Sopenharmony_ci mode == FDP1_FIXED3D || \ 50862306a36Sopenharmony_ci mode == FDP1_NEXTFIELD) 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#define FDP1_DEINT_MODE_USES_PREV(mode) \ 51162306a36Sopenharmony_ci (mode == FDP1_ADAPT2D3D || \ 51262306a36Sopenharmony_ci mode == FDP1_FIXED3D || \ 51362306a36Sopenharmony_ci mode == FDP1_PREVFIELD) 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/* 51662306a36Sopenharmony_ci * FDP1 operates on potentially 3 fields, which are tracked 51762306a36Sopenharmony_ci * from the VB buffers using this context structure. 51862306a36Sopenharmony_ci * Will always be a field or a full frame, never two fields. 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_cistruct fdp1_field_buffer { 52162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vb; 52262306a36Sopenharmony_ci dma_addr_t addrs[3]; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Should be NONE:TOP:BOTTOM only */ 52562306a36Sopenharmony_ci enum v4l2_field field; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Flag to indicate this is the last field in the vb */ 52862306a36Sopenharmony_ci bool last_field; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Buffer queue lists */ 53162306a36Sopenharmony_ci struct list_head list; 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistruct fdp1_buffer { 53562306a36Sopenharmony_ci struct v4l2_m2m_buffer m2m_buf; 53662306a36Sopenharmony_ci struct fdp1_field_buffer fields[2]; 53762306a36Sopenharmony_ci unsigned int num_fields; 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic inline struct fdp1_buffer *to_fdp1_buffer(struct vb2_v4l2_buffer *vb) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci return container_of(vb, struct fdp1_buffer, m2m_buf.vb); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistruct fdp1_job { 54662306a36Sopenharmony_ci struct fdp1_field_buffer *previous; 54762306a36Sopenharmony_ci struct fdp1_field_buffer *active; 54862306a36Sopenharmony_ci struct fdp1_field_buffer *next; 54962306a36Sopenharmony_ci struct fdp1_field_buffer *dst; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* A job can only be on one list at a time */ 55262306a36Sopenharmony_ci struct list_head list; 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistruct fdp1_dev { 55662306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 55762306a36Sopenharmony_ci struct video_device vfd; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci struct mutex dev_mutex; 56062306a36Sopenharmony_ci spinlock_t irqlock; 56162306a36Sopenharmony_ci spinlock_t device_process_lock; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci void __iomem *regs; 56462306a36Sopenharmony_ci unsigned int irq; 56562306a36Sopenharmony_ci struct device *dev; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Job Queues */ 56862306a36Sopenharmony_ci struct fdp1_job jobs[FDP1_NUMBER_JOBS]; 56962306a36Sopenharmony_ci struct list_head free_job_list; 57062306a36Sopenharmony_ci struct list_head queued_job_list; 57162306a36Sopenharmony_ci struct list_head hw_job_list; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci unsigned int clk_rate; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci struct rcar_fcp_device *fcp; 57662306a36Sopenharmony_ci struct v4l2_m2m_dev *m2m_dev; 57762306a36Sopenharmony_ci}; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistruct fdp1_ctx { 58062306a36Sopenharmony_ci struct v4l2_fh fh; 58162306a36Sopenharmony_ci struct fdp1_dev *fdp1; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 58462306a36Sopenharmony_ci unsigned int sequence; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Processed buffers in this transaction */ 58762306a36Sopenharmony_ci u8 num_processed; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Transaction length (i.e. how many buffers per transaction) */ 59062306a36Sopenharmony_ci u32 translen; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Abort requested by m2m */ 59362306a36Sopenharmony_ci int aborting; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Deinterlace processing mode */ 59662306a36Sopenharmony_ci enum fdp1_deint_mode deint_mode; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* 59962306a36Sopenharmony_ci * Adaptive 2D/3D mode uses a shared mask 60062306a36Sopenharmony_ci * This is allocated at streamon, if the ADAPT2D3D mode 60162306a36Sopenharmony_ci * is requested 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci unsigned int smsk_size; 60462306a36Sopenharmony_ci dma_addr_t smsk_addr[2]; 60562306a36Sopenharmony_ci void *smsk_cpu; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Capture pipeline, can specify an alpha value 60862306a36Sopenharmony_ci * for supported formats. 0-255 only 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci unsigned char alpha; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Source and destination queue data */ 61362306a36Sopenharmony_ci struct fdp1_q_data out_q; /* HW Source */ 61462306a36Sopenharmony_ci struct fdp1_q_data cap_q; /* HW Destination */ 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * Field Queues 61862306a36Sopenharmony_ci * Interlaced fields are used on 3 occasions, and tracked in this list. 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * V4L2 Buffers are tracked inside the fdp1_buffer 62162306a36Sopenharmony_ci * and released when the last 'field' completes 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci struct list_head fields_queue; 62462306a36Sopenharmony_ci unsigned int buffers_queued; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* 62762306a36Sopenharmony_ci * For de-interlacing we need to track our previous buffer 62862306a36Sopenharmony_ci * while preparing our job lists. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci struct fdp1_field_buffer *previous; 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic inline struct fdp1_ctx *fh_to_ctx(struct v4l2_fh *fh) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci return container_of(fh, struct fdp1_ctx, fh); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic struct fdp1_q_data *get_q_data(struct fdp1_ctx *ctx, 63962306a36Sopenharmony_ci enum v4l2_buf_type type) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) 64262306a36Sopenharmony_ci return &ctx->out_q; 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci return &ctx->cap_q; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* 64862306a36Sopenharmony_ci * list_remove_job: Take the first item off the specified job list 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * Returns: pointer to a job, or NULL if the list is empty. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_cistatic struct fdp1_job *list_remove_job(struct fdp1_dev *fdp1, 65362306a36Sopenharmony_ci struct list_head *list) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct fdp1_job *job; 65662306a36Sopenharmony_ci unsigned long flags; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci spin_lock_irqsave(&fdp1->irqlock, flags); 65962306a36Sopenharmony_ci job = list_first_entry_or_null(list, struct fdp1_job, list); 66062306a36Sopenharmony_ci if (job) 66162306a36Sopenharmony_ci list_del(&job->list); 66262306a36Sopenharmony_ci spin_unlock_irqrestore(&fdp1->irqlock, flags); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return job; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * list_add_job: Add a job to the specified job list 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Returns: void - always succeeds 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_cistatic void list_add_job(struct fdp1_dev *fdp1, 67362306a36Sopenharmony_ci struct list_head *list, 67462306a36Sopenharmony_ci struct fdp1_job *job) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci unsigned long flags; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci spin_lock_irqsave(&fdp1->irqlock, flags); 67962306a36Sopenharmony_ci list_add_tail(&job->list, list); 68062306a36Sopenharmony_ci spin_unlock_irqrestore(&fdp1->irqlock, flags); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic struct fdp1_job *fdp1_job_alloc(struct fdp1_dev *fdp1) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci return list_remove_job(fdp1, &fdp1->free_job_list); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void fdp1_job_free(struct fdp1_dev *fdp1, struct fdp1_job *job) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci /* Ensure that all residue from previous jobs is gone */ 69162306a36Sopenharmony_ci memset(job, 0, sizeof(struct fdp1_job)); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci list_add_job(fdp1, &fdp1->free_job_list, job); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic void queue_job(struct fdp1_dev *fdp1, struct fdp1_job *job) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci list_add_job(fdp1, &fdp1->queued_job_list, job); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic struct fdp1_job *get_queued_job(struct fdp1_dev *fdp1) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci return list_remove_job(fdp1, &fdp1->queued_job_list); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void queue_hw_job(struct fdp1_dev *fdp1, struct fdp1_job *job) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci list_add_job(fdp1, &fdp1->hw_job_list, job); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic struct fdp1_job *get_hw_queued_job(struct fdp1_dev *fdp1) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci return list_remove_job(fdp1, &fdp1->hw_job_list); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/* 71762306a36Sopenharmony_ci * Buffer lists handling 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_cistatic void fdp1_field_complete(struct fdp1_ctx *ctx, 72062306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci /* job->previous may be on the first field */ 72362306a36Sopenharmony_ci if (!fbuf) 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (fbuf->last_field) 72762306a36Sopenharmony_ci v4l2_m2m_buf_done(fbuf->vb, VB2_BUF_STATE_DONE); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void fdp1_queue_field(struct fdp1_ctx *ctx, 73162306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci unsigned long flags; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci spin_lock_irqsave(&ctx->fdp1->irqlock, flags); 73662306a36Sopenharmony_ci list_add_tail(&fbuf->list, &ctx->fields_queue); 73762306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ctx->buffers_queued++; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic struct fdp1_field_buffer *fdp1_dequeue_field(struct fdp1_ctx *ctx) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf; 74562306a36Sopenharmony_ci unsigned long flags; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci ctx->buffers_queued--; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci spin_lock_irqsave(&ctx->fdp1->irqlock, flags); 75062306a36Sopenharmony_ci fbuf = list_first_entry_or_null(&ctx->fields_queue, 75162306a36Sopenharmony_ci struct fdp1_field_buffer, list); 75262306a36Sopenharmony_ci if (fbuf) 75362306a36Sopenharmony_ci list_del(&fbuf->list); 75462306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return fbuf; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/* 76062306a36Sopenharmony_ci * Return the next field in the queue - or NULL, 76162306a36Sopenharmony_ci * without removing the item from the list 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic struct fdp1_field_buffer *fdp1_peek_queued_field(struct fdp1_ctx *ctx) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf; 76662306a36Sopenharmony_ci unsigned long flags; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci spin_lock_irqsave(&ctx->fdp1->irqlock, flags); 76962306a36Sopenharmony_ci fbuf = list_first_entry_or_null(&ctx->fields_queue, 77062306a36Sopenharmony_ci struct fdp1_field_buffer, list); 77162306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return fbuf; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic u32 fdp1_read(struct fdp1_dev *fdp1, unsigned int reg) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci u32 value = ioread32(fdp1->regs + reg); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (debug >= 2) 78162306a36Sopenharmony_ci dprintk(fdp1, "Read 0x%08x from 0x%04x\n", value, reg); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return value; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void fdp1_write(struct fdp1_dev *fdp1, u32 val, unsigned int reg) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci if (debug >= 2) 78962306a36Sopenharmony_ci dprintk(fdp1, "Write 0x%08x to 0x%04x\n", val, reg); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci iowrite32(val, fdp1->regs + reg); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/* IPC registers are to be programmed with constant values */ 79562306a36Sopenharmony_cistatic void fdp1_set_ipc_dli(struct fdp1_ctx *ctx) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_SMSK_THRESH_CONST, FD1_IPC_SMSK_THRESH); 80062306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_COMB_DET_CONST, FD1_IPC_COMB_DET); 80162306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_MOTDEC_CONST, FD1_IPC_MOTDEC); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_BLEND_CONST, FD1_IPC_DLI_BLEND); 80462306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_HGAIN_CONST, FD1_IPC_DLI_HGAIN); 80562306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_SPRS_CONST, FD1_IPC_DLI_SPRS); 80662306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_ANGLE_CONST, FD1_IPC_DLI_ANGLE); 80762306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_ISOPIX0_CONST, FD1_IPC_DLI_ISOPIX0); 80862306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_DLI_ISOPIX1_CONST, FD1_IPC_DLI_ISOPIX1); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void fdp1_set_ipc_sensor(struct fdp1_ctx *ctx) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 81562306a36Sopenharmony_ci struct fdp1_q_data *src_q_data = &ctx->out_q; 81662306a36Sopenharmony_ci unsigned int x0, x1; 81762306a36Sopenharmony_ci unsigned int hsize = src_q_data->format.width; 81862306a36Sopenharmony_ci unsigned int vsize = src_q_data->format.height; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci x0 = hsize / 3; 82162306a36Sopenharmony_ci x1 = 2 * hsize / 3; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_SENSOR_TH0_CONST, FD1_IPC_SENSOR_TH0); 82462306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_SENSOR_TH1_CONST, FD1_IPC_SENSOR_TH1); 82562306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_SENSOR_CTL0_CONST, FD1_IPC_SENSOR_CTL0); 82662306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_SENSOR_CTL1_CONST, FD1_IPC_SENSOR_CTL1); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci fdp1_write(fdp1, ((hsize - 1) << FD1_IPC_SENSOR_CTL2_X_SHIFT) | 82962306a36Sopenharmony_ci ((vsize - 1) << FD1_IPC_SENSOR_CTL2_Y_SHIFT), 83062306a36Sopenharmony_ci FD1_IPC_SENSOR_CTL2); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci fdp1_write(fdp1, (x0 << FD1_IPC_SENSOR_CTL3_0_SHIFT) | 83362306a36Sopenharmony_ci (x1 << FD1_IPC_SENSOR_CTL3_1_SHIFT), 83462306a36Sopenharmony_ci FD1_IPC_SENSOR_CTL3); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/* 83862306a36Sopenharmony_ci * fdp1_write_lut: Write a padded LUT to the hw 83962306a36Sopenharmony_ci * 84062306a36Sopenharmony_ci * FDP1 uses constant data for de-interlacing processing, 84162306a36Sopenharmony_ci * with large tables. These hardware tables are all 256 bytes 84262306a36Sopenharmony_ci * long, however they often contain repeated data at the end. 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci * The last byte of the table is written to all remaining entries. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_cistatic void fdp1_write_lut(struct fdp1_dev *fdp1, const u8 *lut, 84762306a36Sopenharmony_ci unsigned int len, unsigned int base) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci unsigned int i; 85062306a36Sopenharmony_ci u8 pad; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Tables larger than the hw are clipped */ 85362306a36Sopenharmony_ci len = min(len, 256u); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci for (i = 0; i < len; i++) 85662306a36Sopenharmony_ci fdp1_write(fdp1, lut[i], base + (i*4)); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Tables are padded with the last entry */ 85962306a36Sopenharmony_ci pad = lut[i-1]; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci for (; i < 256; i++) 86262306a36Sopenharmony_ci fdp1_write(fdp1, pad, base + (i*4)); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void fdp1_set_lut(struct fdp1_dev *fdp1) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci fdp1_write_lut(fdp1, fdp1_diff_adj, ARRAY_SIZE(fdp1_diff_adj), 86862306a36Sopenharmony_ci FD1_LUT_DIF_ADJ); 86962306a36Sopenharmony_ci fdp1_write_lut(fdp1, fdp1_sad_adj, ARRAY_SIZE(fdp1_sad_adj), 87062306a36Sopenharmony_ci FD1_LUT_SAD_ADJ); 87162306a36Sopenharmony_ci fdp1_write_lut(fdp1, fdp1_bld_gain, ARRAY_SIZE(fdp1_bld_gain), 87262306a36Sopenharmony_ci FD1_LUT_BLD_GAIN); 87362306a36Sopenharmony_ci fdp1_write_lut(fdp1, fdp1_dif_gain, ARRAY_SIZE(fdp1_dif_gain), 87462306a36Sopenharmony_ci FD1_LUT_DIF_GAIN); 87562306a36Sopenharmony_ci fdp1_write_lut(fdp1, fdp1_mdet, ARRAY_SIZE(fdp1_mdet), 87662306a36Sopenharmony_ci FD1_LUT_MDET); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void fdp1_configure_rpf(struct fdp1_ctx *ctx, 88062306a36Sopenharmony_ci struct fdp1_job *job) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 88362306a36Sopenharmony_ci u32 picture_size; 88462306a36Sopenharmony_ci u32 pstride; 88562306a36Sopenharmony_ci u32 format; 88662306a36Sopenharmony_ci u32 smsk_addr; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci struct fdp1_q_data *q_data = &ctx->out_q; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* Picture size is common to Source and Destination frames */ 89162306a36Sopenharmony_ci picture_size = (q_data->format.width << FD1_RPF_SIZE_H_SHIFT) 89262306a36Sopenharmony_ci | (q_data->vsize << FD1_RPF_SIZE_V_SHIFT); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Strides */ 89562306a36Sopenharmony_ci pstride = q_data->stride_y << FD1_RPF_PSTRIDE_Y_SHIFT; 89662306a36Sopenharmony_ci if (q_data->format.num_planes > 1) 89762306a36Sopenharmony_ci pstride |= q_data->stride_c << FD1_RPF_PSTRIDE_C_SHIFT; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Format control */ 90062306a36Sopenharmony_ci format = q_data->fmt->fmt; 90162306a36Sopenharmony_ci if (q_data->fmt->swap_yc) 90262306a36Sopenharmony_ci format |= FD1_RPF_FORMAT_RSPYCS; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (q_data->fmt->swap_uv) 90562306a36Sopenharmony_ci format |= FD1_RPF_FORMAT_RSPUVS; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (job->active->field == V4L2_FIELD_BOTTOM) { 90862306a36Sopenharmony_ci format |= FD1_RPF_FORMAT_CF; /* Set for Bottom field */ 90962306a36Sopenharmony_ci smsk_addr = ctx->smsk_addr[0]; 91062306a36Sopenharmony_ci } else { 91162306a36Sopenharmony_ci smsk_addr = ctx->smsk_addr[1]; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Deint mode is non-zero when deinterlacing */ 91562306a36Sopenharmony_ci if (ctx->deint_mode) 91662306a36Sopenharmony_ci format |= FD1_RPF_FORMAT_CIPM; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci fdp1_write(fdp1, format, FD1_RPF_FORMAT); 91962306a36Sopenharmony_ci fdp1_write(fdp1, q_data->fmt->swap, FD1_RPF_SWAP); 92062306a36Sopenharmony_ci fdp1_write(fdp1, picture_size, FD1_RPF_SIZE); 92162306a36Sopenharmony_ci fdp1_write(fdp1, pstride, FD1_RPF_PSTRIDE); 92262306a36Sopenharmony_ci fdp1_write(fdp1, smsk_addr, FD1_RPF_SMSK_ADDR); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Previous Field Channel (CH0) */ 92562306a36Sopenharmony_ci if (job->previous) 92662306a36Sopenharmony_ci fdp1_write(fdp1, job->previous->addrs[0], FD1_RPF0_ADDR_Y); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* Current Field Channel (CH1) */ 92962306a36Sopenharmony_ci fdp1_write(fdp1, job->active->addrs[0], FD1_RPF1_ADDR_Y); 93062306a36Sopenharmony_ci fdp1_write(fdp1, job->active->addrs[1], FD1_RPF1_ADDR_C0); 93162306a36Sopenharmony_ci fdp1_write(fdp1, job->active->addrs[2], FD1_RPF1_ADDR_C1); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Next Field Channel (CH2) */ 93462306a36Sopenharmony_ci if (job->next) 93562306a36Sopenharmony_ci fdp1_write(fdp1, job->next->addrs[0], FD1_RPF2_ADDR_Y); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void fdp1_configure_wpf(struct fdp1_ctx *ctx, 93962306a36Sopenharmony_ci struct fdp1_job *job) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 94262306a36Sopenharmony_ci struct fdp1_q_data *src_q_data = &ctx->out_q; 94362306a36Sopenharmony_ci struct fdp1_q_data *q_data = &ctx->cap_q; 94462306a36Sopenharmony_ci u32 pstride; 94562306a36Sopenharmony_ci u32 format; 94662306a36Sopenharmony_ci u32 swap; 94762306a36Sopenharmony_ci u32 rndctl; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci pstride = q_data->format.plane_fmt[0].bytesperline 95062306a36Sopenharmony_ci << FD1_WPF_PSTRIDE_Y_SHIFT; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (q_data->format.num_planes > 1) 95362306a36Sopenharmony_ci pstride |= q_data->format.plane_fmt[1].bytesperline 95462306a36Sopenharmony_ci << FD1_WPF_PSTRIDE_C_SHIFT; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci format = q_data->fmt->fmt; /* Output Format Code */ 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (q_data->fmt->swap_yc) 95962306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_WSPYCS; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (q_data->fmt->swap_uv) 96262306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_WSPUVS; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (fdp1_fmt_is_rgb(q_data->fmt)) { 96562306a36Sopenharmony_ci /* Enable Colour Space conversion */ 96662306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_CSC; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Set WRTM */ 96962306a36Sopenharmony_ci if (src_q_data->format.ycbcr_enc == V4L2_YCBCR_ENC_709) 97062306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_WRTM_709_16; 97162306a36Sopenharmony_ci else if (src_q_data->format.quantization == 97262306a36Sopenharmony_ci V4L2_QUANTIZATION_FULL_RANGE) 97362306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_WRTM_601_0; 97462306a36Sopenharmony_ci else 97562306a36Sopenharmony_ci format |= FD1_WPF_FORMAT_WRTM_601_16; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Set an alpha value into the Pad Value */ 97962306a36Sopenharmony_ci format |= ctx->alpha << FD1_WPF_FORMAT_PDV_SHIFT; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* Determine picture rounding and clipping */ 98262306a36Sopenharmony_ci rndctl = FD1_WPF_RNDCTL_CBRM; /* Rounding Off */ 98362306a36Sopenharmony_ci rndctl |= FD1_WPF_RNDCTL_CLMD_NOCLIP; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* WPF Swap needs both ISWAP and OSWAP setting */ 98662306a36Sopenharmony_ci swap = q_data->fmt->swap << FD1_WPF_SWAP_OSWAP_SHIFT; 98762306a36Sopenharmony_ci swap |= src_q_data->fmt->swap << FD1_WPF_SWAP_SSWAP_SHIFT; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci fdp1_write(fdp1, format, FD1_WPF_FORMAT); 99062306a36Sopenharmony_ci fdp1_write(fdp1, rndctl, FD1_WPF_RNDCTL); 99162306a36Sopenharmony_ci fdp1_write(fdp1, swap, FD1_WPF_SWAP); 99262306a36Sopenharmony_ci fdp1_write(fdp1, pstride, FD1_WPF_PSTRIDE); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci fdp1_write(fdp1, job->dst->addrs[0], FD1_WPF_ADDR_Y); 99562306a36Sopenharmony_ci fdp1_write(fdp1, job->dst->addrs[1], FD1_WPF_ADDR_C0); 99662306a36Sopenharmony_ci fdp1_write(fdp1, job->dst->addrs[2], FD1_WPF_ADDR_C1); 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void fdp1_configure_deint_mode(struct fdp1_ctx *ctx, 100062306a36Sopenharmony_ci struct fdp1_job *job) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 100362306a36Sopenharmony_ci u32 opmode = FD1_CTL_OPMODE_VIMD_NOINTERRUPT; 100462306a36Sopenharmony_ci u32 ipcmode = FD1_IPC_MODE_DLI; /* Always set */ 100562306a36Sopenharmony_ci u32 channels = FD1_CTL_CHACT_WR | FD1_CTL_CHACT_RD1; /* Always on */ 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* De-interlacing Mode */ 100862306a36Sopenharmony_ci switch (ctx->deint_mode) { 100962306a36Sopenharmony_ci default: 101062306a36Sopenharmony_ci case FDP1_PROGRESSIVE: 101162306a36Sopenharmony_ci dprintk(fdp1, "Progressive Mode\n"); 101262306a36Sopenharmony_ci opmode |= FD1_CTL_OPMODE_PRG; 101362306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_FIXED2D; 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci case FDP1_ADAPT2D3D: 101662306a36Sopenharmony_ci dprintk(fdp1, "Adapt2D3D Mode\n"); 101762306a36Sopenharmony_ci if (ctx->sequence == 0 || ctx->aborting) 101862306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_FIXED2D; 101962306a36Sopenharmony_ci else 102062306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_ADAPT2D3D; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (ctx->sequence > 1) { 102362306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_SMW; 102462306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_RD0 | FD1_CTL_CHACT_RD2; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (ctx->sequence > 2) 102862306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_SMR; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci case FDP1_FIXED3D: 103262306a36Sopenharmony_ci dprintk(fdp1, "Fixed 3D Mode\n"); 103362306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_FIXED3D; 103462306a36Sopenharmony_ci /* Except for first and last frame, enable all channels */ 103562306a36Sopenharmony_ci if (!(ctx->sequence == 0 || ctx->aborting)) 103662306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_RD0 | FD1_CTL_CHACT_RD2; 103762306a36Sopenharmony_ci break; 103862306a36Sopenharmony_ci case FDP1_FIXED2D: 103962306a36Sopenharmony_ci dprintk(fdp1, "Fixed 2D Mode\n"); 104062306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_FIXED2D; 104162306a36Sopenharmony_ci /* No extra channels enabled */ 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci case FDP1_PREVFIELD: 104462306a36Sopenharmony_ci dprintk(fdp1, "Previous Field Mode\n"); 104562306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_PREVFIELD; 104662306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_RD0; /* Previous */ 104762306a36Sopenharmony_ci break; 104862306a36Sopenharmony_ci case FDP1_NEXTFIELD: 104962306a36Sopenharmony_ci dprintk(fdp1, "Next Field Mode\n"); 105062306a36Sopenharmony_ci ipcmode |= FD1_IPC_MODE_DIM_NEXTFIELD; 105162306a36Sopenharmony_ci channels |= FD1_CTL_CHACT_RD2; /* Next */ 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci fdp1_write(fdp1, channels, FD1_CTL_CHACT); 105662306a36Sopenharmony_ci fdp1_write(fdp1, opmode, FD1_CTL_OPMODE); 105762306a36Sopenharmony_ci fdp1_write(fdp1, ipcmode, FD1_IPC_MODE); 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/* 106162306a36Sopenharmony_ci * fdp1_device_process() - Run the hardware 106262306a36Sopenharmony_ci * 106362306a36Sopenharmony_ci * Configure and start the hardware to generate a single frame 106462306a36Sopenharmony_ci * of output given our input parameters. 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_cistatic int fdp1_device_process(struct fdp1_ctx *ctx) 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 107062306a36Sopenharmony_ci struct fdp1_job *job; 107162306a36Sopenharmony_ci unsigned long flags; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci spin_lock_irqsave(&fdp1->device_process_lock, flags); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* Get a job to process */ 107662306a36Sopenharmony_ci job = get_queued_job(fdp1); 107762306a36Sopenharmony_ci if (!job) { 107862306a36Sopenharmony_ci /* 107962306a36Sopenharmony_ci * VINT can call us to see if we can queue another job. 108062306a36Sopenharmony_ci * If we have no work to do, we simply return. 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci spin_unlock_irqrestore(&fdp1->device_process_lock, flags); 108362306a36Sopenharmony_ci return 0; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* First Frame only? ... */ 108762306a36Sopenharmony_ci fdp1_write(fdp1, FD1_CTL_CLKCTRL_CSTP_N, FD1_CTL_CLKCTRL); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Set the mode, and configuration */ 109062306a36Sopenharmony_ci fdp1_configure_deint_mode(ctx, job); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* DLI Static Configuration */ 109362306a36Sopenharmony_ci fdp1_set_ipc_dli(ctx); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Sensor Configuration */ 109662306a36Sopenharmony_ci fdp1_set_ipc_sensor(ctx); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* Setup the source picture */ 109962306a36Sopenharmony_ci fdp1_configure_rpf(ctx, job); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Setup the destination picture */ 110262306a36Sopenharmony_ci fdp1_configure_wpf(ctx, job); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* Line Memory Pixel Number Register for linear access */ 110562306a36Sopenharmony_ci fdp1_write(fdp1, FD1_IPC_LMEM_LINEAR, FD1_IPC_LMEM); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Enable Interrupts */ 110862306a36Sopenharmony_ci fdp1_write(fdp1, FD1_CTL_IRQ_MASK, FD1_CTL_IRQENB); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* Finally, the Immediate Registers */ 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* This job is now in the HW queue */ 111362306a36Sopenharmony_ci queue_hw_job(fdp1, job); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* Start the command */ 111662306a36Sopenharmony_ci fdp1_write(fdp1, FD1_CTL_CMD_STRCMD, FD1_CTL_CMD); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* Registers will update to HW at next VINT */ 111962306a36Sopenharmony_ci fdp1_write(fdp1, FD1_CTL_REGEND_REGEND, FD1_CTL_REGEND); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* Enable VINT Generator */ 112262306a36Sopenharmony_ci fdp1_write(fdp1, FD1_CTL_SGCMD_SGEN, FD1_CTL_SGCMD); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci spin_unlock_irqrestore(&fdp1->device_process_lock, flags); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return 0; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci/* 113062306a36Sopenharmony_ci * mem2mem callbacks 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci/* 113462306a36Sopenharmony_ci * job_ready() - check whether an instance is ready to be scheduled to run 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_cistatic int fdp1_m2m_job_ready(void *priv) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct fdp1_ctx *ctx = priv; 113962306a36Sopenharmony_ci struct fdp1_q_data *src_q_data = &ctx->out_q; 114062306a36Sopenharmony_ci int srcbufs = 1; 114162306a36Sopenharmony_ci int dstbufs = 1; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci dprintk(ctx->fdp1, "+ Src: %d : Dst: %d\n", 114462306a36Sopenharmony_ci v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx), 114562306a36Sopenharmony_ci v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* One output buffer is required for each field */ 114862306a36Sopenharmony_ci if (V4L2_FIELD_HAS_BOTH(src_q_data->format.field)) 114962306a36Sopenharmony_ci dstbufs = 2; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < srcbufs 115262306a36Sopenharmony_ci || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < dstbufs) { 115362306a36Sopenharmony_ci dprintk(ctx->fdp1, "Not enough buffers available\n"); 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci return 1; 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_cistatic void fdp1_m2m_job_abort(void *priv) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct fdp1_ctx *ctx = priv; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci dprintk(ctx->fdp1, "+\n"); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Will cancel the transaction in the next interrupt handler */ 116762306a36Sopenharmony_ci ctx->aborting = 1; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Immediate abort sequence */ 117062306a36Sopenharmony_ci fdp1_write(ctx->fdp1, 0, FD1_CTL_SGCMD); 117162306a36Sopenharmony_ci fdp1_write(ctx->fdp1, FD1_CTL_SRESET_SRST, FD1_CTL_SRESET); 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/* 117562306a36Sopenharmony_ci * fdp1_prepare_job: Prepare and queue a new job for a single action of work 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * Prepare the next field, (or frame in progressive) and an output 117862306a36Sopenharmony_ci * buffer for the hardware to perform a single operation. 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_cistatic struct fdp1_job *fdp1_prepare_job(struct fdp1_ctx *ctx) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 118362306a36Sopenharmony_ci struct fdp1_buffer *fbuf; 118462306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 118562306a36Sopenharmony_ci struct fdp1_job *job; 118662306a36Sopenharmony_ci unsigned int buffers_required = 1; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci dprintk(fdp1, "+\n"); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (FDP1_DEINT_MODE_USES_NEXT(ctx->deint_mode)) 119162306a36Sopenharmony_ci buffers_required = 2; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (ctx->buffers_queued < buffers_required) 119462306a36Sopenharmony_ci return NULL; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci job = fdp1_job_alloc(fdp1); 119762306a36Sopenharmony_ci if (!job) { 119862306a36Sopenharmony_ci dprintk(fdp1, "No free jobs currently available\n"); 119962306a36Sopenharmony_ci return NULL; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci job->active = fdp1_dequeue_field(ctx); 120362306a36Sopenharmony_ci if (!job->active) { 120462306a36Sopenharmony_ci /* Buffer check should prevent this ever happening */ 120562306a36Sopenharmony_ci dprintk(fdp1, "No input buffers currently available\n"); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci fdp1_job_free(fdp1, job); 120862306a36Sopenharmony_ci return NULL; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci dprintk(fdp1, "+ Buffer en-route...\n"); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Source buffers have been prepared on our buffer_queue 121462306a36Sopenharmony_ci * Prepare our Output buffer 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 121762306a36Sopenharmony_ci fbuf = to_fdp1_buffer(vbuf); 121862306a36Sopenharmony_ci job->dst = &fbuf->fields[0]; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci job->active->vb->sequence = ctx->sequence; 122162306a36Sopenharmony_ci job->dst->vb->sequence = ctx->sequence; 122262306a36Sopenharmony_ci ctx->sequence++; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode)) { 122562306a36Sopenharmony_ci job->previous = ctx->previous; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* Active buffer becomes the next job's previous buffer */ 122862306a36Sopenharmony_ci ctx->previous = job->active; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (FDP1_DEINT_MODE_USES_NEXT(ctx->deint_mode)) { 123262306a36Sopenharmony_ci /* Must be called after 'active' is dequeued */ 123362306a36Sopenharmony_ci job->next = fdp1_peek_queued_field(ctx); 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* Transfer timestamps and flags from src->dst */ 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci job->dst->vb->vb2_buf.timestamp = job->active->vb->vb2_buf.timestamp; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci job->dst->vb->flags = job->active->vb->flags & 124162306a36Sopenharmony_ci V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* Ideally, the frame-end function will just 'check' to see 124462306a36Sopenharmony_ci * if there are more jobs instead 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci ctx->translen++; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* Finally, Put this job on the processing queue */ 124962306a36Sopenharmony_ci queue_job(fdp1, job); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci dprintk(fdp1, "Job Queued translen = %d\n", ctx->translen); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return job; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci/* fdp1_m2m_device_run() - prepares and starts the device for an M2M task 125762306a36Sopenharmony_ci * 125862306a36Sopenharmony_ci * A single input buffer is taken and serialised into our fdp1_buffer 125962306a36Sopenharmony_ci * queue. The queue is then processed to create as many jobs as possible 126062306a36Sopenharmony_ci * from our available input. 126162306a36Sopenharmony_ci */ 126262306a36Sopenharmony_cistatic void fdp1_m2m_device_run(void *priv) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct fdp1_ctx *ctx = priv; 126562306a36Sopenharmony_ci struct fdp1_dev *fdp1 = ctx->fdp1; 126662306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb; 126762306a36Sopenharmony_ci struct fdp1_buffer *buf; 126862306a36Sopenharmony_ci unsigned int i; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci dprintk(fdp1, "+\n"); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci ctx->translen = 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /* Get our incoming buffer of either one or two fields, or one frame */ 127562306a36Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 127662306a36Sopenharmony_ci buf = to_fdp1_buffer(src_vb); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci for (i = 0; i < buf->num_fields; i++) { 127962306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf = &buf->fields[i]; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci fdp1_queue_field(ctx, fbuf); 128262306a36Sopenharmony_ci dprintk(fdp1, "Queued Buffer [%d] last_field:%d\n", 128362306a36Sopenharmony_ci i, fbuf->last_field); 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci /* Queue as many jobs as our data provides for */ 128762306a36Sopenharmony_ci while (fdp1_prepare_job(ctx)) 128862306a36Sopenharmony_ci ; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (ctx->translen == 0) { 129162306a36Sopenharmony_ci dprintk(fdp1, "No jobs were processed. M2M action complete\n"); 129262306a36Sopenharmony_ci v4l2_m2m_job_finish(fdp1->m2m_dev, ctx->fh.m2m_ctx); 129362306a36Sopenharmony_ci return; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Kick the job processing action */ 129762306a36Sopenharmony_ci fdp1_device_process(ctx); 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci/* 130162306a36Sopenharmony_ci * device_frame_end: 130262306a36Sopenharmony_ci * 130362306a36Sopenharmony_ci * Handles the M2M level after a buffer completion event. 130462306a36Sopenharmony_ci */ 130562306a36Sopenharmony_cistatic void device_frame_end(struct fdp1_dev *fdp1, 130662306a36Sopenharmony_ci enum vb2_buffer_state state) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci struct fdp1_ctx *ctx; 130962306a36Sopenharmony_ci unsigned long flags; 131062306a36Sopenharmony_ci struct fdp1_job *job = get_hw_queued_job(fdp1); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci dprintk(fdp1, "+\n"); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(fdp1->m2m_dev); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (ctx == NULL) { 131762306a36Sopenharmony_ci v4l2_err(&fdp1->v4l2_dev, 131862306a36Sopenharmony_ci "Instance released before the end of transaction\n"); 131962306a36Sopenharmony_ci return; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci ctx->num_processed++; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci * fdp1_field_complete will call buf_done only when the last vb2_buffer 132662306a36Sopenharmony_ci * reference is complete 132762306a36Sopenharmony_ci */ 132862306a36Sopenharmony_ci if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode)) 132962306a36Sopenharmony_ci fdp1_field_complete(ctx, job->previous); 133062306a36Sopenharmony_ci else 133162306a36Sopenharmony_ci fdp1_field_complete(ctx, job->active); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci spin_lock_irqsave(&fdp1->irqlock, flags); 133462306a36Sopenharmony_ci v4l2_m2m_buf_done(job->dst->vb, state); 133562306a36Sopenharmony_ci job->dst = NULL; 133662306a36Sopenharmony_ci spin_unlock_irqrestore(&fdp1->irqlock, flags); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Move this job back to the free job list */ 133962306a36Sopenharmony_ci fdp1_job_free(fdp1, job); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci dprintk(fdp1, "curr_ctx->num_processed %d curr_ctx->translen %d\n", 134262306a36Sopenharmony_ci ctx->num_processed, ctx->translen); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci if (ctx->num_processed == ctx->translen || 134562306a36Sopenharmony_ci ctx->aborting) { 134662306a36Sopenharmony_ci dprintk(ctx->fdp1, "Finishing transaction\n"); 134762306a36Sopenharmony_ci ctx->num_processed = 0; 134862306a36Sopenharmony_ci v4l2_m2m_job_finish(fdp1->m2m_dev, ctx->fh.m2m_ctx); 134962306a36Sopenharmony_ci } else { 135062306a36Sopenharmony_ci /* 135162306a36Sopenharmony_ci * For pipelined performance support, this would 135262306a36Sopenharmony_ci * be called from a VINT handler 135362306a36Sopenharmony_ci */ 135462306a36Sopenharmony_ci fdp1_device_process(ctx); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/* 135962306a36Sopenharmony_ci * video ioctls 136062306a36Sopenharmony_ci */ 136162306a36Sopenharmony_cistatic int fdp1_vidioc_querycap(struct file *file, void *priv, 136262306a36Sopenharmony_ci struct v4l2_capability *cap) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci strscpy(cap->driver, DRIVER_NAME, sizeof(cap->driver)); 136562306a36Sopenharmony_ci strscpy(cap->card, DRIVER_NAME, sizeof(cap->card)); 136662306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 136762306a36Sopenharmony_ci "platform:%s", DRIVER_NAME); 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int fdp1_enum_fmt(struct v4l2_fmtdesc *f, u32 type) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci unsigned int i, num; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci num = 0; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fdp1_formats); ++i) { 137862306a36Sopenharmony_ci if (fdp1_formats[i].types & type) { 137962306a36Sopenharmony_ci if (num == f->index) 138062306a36Sopenharmony_ci break; 138162306a36Sopenharmony_ci ++num; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci /* Format not found */ 138662306a36Sopenharmony_ci if (i >= ARRAY_SIZE(fdp1_formats)) 138762306a36Sopenharmony_ci return -EINVAL; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* Format found */ 139062306a36Sopenharmony_ci f->pixelformat = fdp1_formats[i].fourcc; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci return 0; 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic int fdp1_enum_fmt_vid_cap(struct file *file, void *priv, 139662306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci return fdp1_enum_fmt(f, FDP1_CAPTURE); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic int fdp1_enum_fmt_vid_out(struct file *file, void *priv, 140262306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci return fdp1_enum_fmt(f, FDP1_OUTPUT); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int fdp1_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct fdp1_q_data *q_data; 141062306a36Sopenharmony_ci struct fdp1_ctx *ctx = fh_to_ctx(priv); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) 141362306a36Sopenharmony_ci return -EINVAL; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 141662306a36Sopenharmony_ci f->fmt.pix_mp = q_data->format; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci return 0; 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix, 142262306a36Sopenharmony_ci const struct fdp1_fmt *fmt) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci unsigned int i; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci /* Compute and clamp the stride and image size. */ 142762306a36Sopenharmony_ci for (i = 0; i < min_t(unsigned int, fmt->num_planes, 2U); ++i) { 142862306a36Sopenharmony_ci unsigned int hsub = i > 0 ? fmt->hsub : 1; 142962306a36Sopenharmony_ci unsigned int vsub = i > 0 ? fmt->vsub : 1; 143062306a36Sopenharmony_ci /* From VSP : TODO: Confirm alignment limits for FDP1 */ 143162306a36Sopenharmony_ci unsigned int align = 128; 143262306a36Sopenharmony_ci unsigned int bpl; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline, 143562306a36Sopenharmony_ci pix->width / hsub * fmt->bpp[i] / 8, 143662306a36Sopenharmony_ci round_down(FDP1_MAX_STRIDE, align)); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci pix->plane_fmt[i].bytesperline = round_up(bpl, align); 143962306a36Sopenharmony_ci pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline 144062306a36Sopenharmony_ci * pix->height / vsub; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (fmt->num_planes == 3) { 144562306a36Sopenharmony_ci /* The two chroma planes must have the same stride. */ 144662306a36Sopenharmony_ci pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; 144762306a36Sopenharmony_ci pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic void fdp1_try_fmt_output(struct fdp1_ctx *ctx, 145362306a36Sopenharmony_ci const struct fdp1_fmt **fmtinfo, 145462306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci const struct fdp1_fmt *fmt; 145762306a36Sopenharmony_ci unsigned int width; 145862306a36Sopenharmony_ci unsigned int height; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* Validate the pixel format to ensure the output queue supports it. */ 146162306a36Sopenharmony_ci fmt = fdp1_find_format(pix->pixelformat); 146262306a36Sopenharmony_ci if (!fmt || !(fmt->types & FDP1_OUTPUT)) 146362306a36Sopenharmony_ci fmt = fdp1_find_format(V4L2_PIX_FMT_YUYV); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (fmtinfo) 146662306a36Sopenharmony_ci *fmtinfo = fmt; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 146962306a36Sopenharmony_ci pix->num_planes = fmt->num_planes; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* 147262306a36Sopenharmony_ci * Progressive video and all interlaced field orders are acceptable. 147362306a36Sopenharmony_ci * Default to V4L2_FIELD_INTERLACED. 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_ci if (pix->field != V4L2_FIELD_NONE && 147662306a36Sopenharmony_ci pix->field != V4L2_FIELD_ALTERNATE && 147762306a36Sopenharmony_ci !V4L2_FIELD_HAS_BOTH(pix->field)) 147862306a36Sopenharmony_ci pix->field = V4L2_FIELD_INTERLACED; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci /* 148162306a36Sopenharmony_ci * The deinterlacer doesn't care about the colorspace, accept all values 148262306a36Sopenharmony_ci * and default to V4L2_COLORSPACE_SMPTE170M. The YUV to RGB conversion 148362306a36Sopenharmony_ci * at the output of the deinterlacer supports a subset of encodings and 148462306a36Sopenharmony_ci * quantization methods and will only be available when the colorspace 148562306a36Sopenharmony_ci * allows it. 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) 148862306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SMPTE170M; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* 149162306a36Sopenharmony_ci * Align the width and height for YUV 4:2:2 and 4:2:0 formats and clamp 149262306a36Sopenharmony_ci * them to the supported frame size range. The height boundary are 149362306a36Sopenharmony_ci * related to the full frame, divide them by two when the format passes 149462306a36Sopenharmony_ci * fields in separate buffers. 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_ci width = round_down(pix->width, fmt->hsub); 149762306a36Sopenharmony_ci pix->width = clamp(width, FDP1_MIN_W, FDP1_MAX_W); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci height = round_down(pix->height, fmt->vsub); 150062306a36Sopenharmony_ci if (pix->field == V4L2_FIELD_ALTERNATE) 150162306a36Sopenharmony_ci pix->height = clamp(height, FDP1_MIN_H / 2, FDP1_MAX_H / 2); 150262306a36Sopenharmony_ci else 150362306a36Sopenharmony_ci pix->height = clamp(height, FDP1_MIN_H, FDP1_MAX_H); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci fdp1_compute_stride(pix, fmt); 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_cistatic void fdp1_try_fmt_capture(struct fdp1_ctx *ctx, 150962306a36Sopenharmony_ci const struct fdp1_fmt **fmtinfo, 151062306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct fdp1_q_data *src_data = &ctx->out_q; 151362306a36Sopenharmony_ci enum v4l2_colorspace colorspace; 151462306a36Sopenharmony_ci enum v4l2_ycbcr_encoding ycbcr_enc; 151562306a36Sopenharmony_ci enum v4l2_quantization quantization; 151662306a36Sopenharmony_ci const struct fdp1_fmt *fmt; 151762306a36Sopenharmony_ci bool allow_rgb; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* 152062306a36Sopenharmony_ci * Validate the pixel format. We can only accept RGB output formats if 152162306a36Sopenharmony_ci * the input encoding and quantization are compatible with the format 152262306a36Sopenharmony_ci * conversions supported by the hardware. The supported combinations are 152362306a36Sopenharmony_ci * 152462306a36Sopenharmony_ci * V4L2_YCBCR_ENC_601 + V4L2_QUANTIZATION_LIM_RANGE 152562306a36Sopenharmony_ci * V4L2_YCBCR_ENC_601 + V4L2_QUANTIZATION_FULL_RANGE 152662306a36Sopenharmony_ci * V4L2_YCBCR_ENC_709 + V4L2_QUANTIZATION_LIM_RANGE 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci colorspace = src_data->format.colorspace; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci ycbcr_enc = src_data->format.ycbcr_enc; 153162306a36Sopenharmony_ci if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) 153262306a36Sopenharmony_ci ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci quantization = src_data->format.quantization; 153562306a36Sopenharmony_ci if (quantization == V4L2_QUANTIZATION_DEFAULT) 153662306a36Sopenharmony_ci quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false, colorspace, 153762306a36Sopenharmony_ci ycbcr_enc); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci allow_rgb = ycbcr_enc == V4L2_YCBCR_ENC_601 || 154062306a36Sopenharmony_ci (ycbcr_enc == V4L2_YCBCR_ENC_709 && 154162306a36Sopenharmony_ci quantization == V4L2_QUANTIZATION_LIM_RANGE); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci fmt = fdp1_find_format(pix->pixelformat); 154462306a36Sopenharmony_ci if (!fmt || (!allow_rgb && fdp1_fmt_is_rgb(fmt))) 154562306a36Sopenharmony_ci fmt = fdp1_find_format(V4L2_PIX_FMT_YUYV); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (fmtinfo) 154862306a36Sopenharmony_ci *fmtinfo = fmt; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 155162306a36Sopenharmony_ci pix->num_planes = fmt->num_planes; 155262306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* 155562306a36Sopenharmony_ci * The colorspace on the capture queue is copied from the output queue 155662306a36Sopenharmony_ci * as the hardware can't change the colorspace. It can convert YCbCr to 155762306a36Sopenharmony_ci * RGB though, in which case the encoding and quantization are set to 155862306a36Sopenharmony_ci * default values as anything else wouldn't make sense. 155962306a36Sopenharmony_ci */ 156062306a36Sopenharmony_ci pix->colorspace = src_data->format.colorspace; 156162306a36Sopenharmony_ci pix->xfer_func = src_data->format.xfer_func; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (fdp1_fmt_is_rgb(fmt)) { 156462306a36Sopenharmony_ci pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 156562306a36Sopenharmony_ci pix->quantization = V4L2_QUANTIZATION_DEFAULT; 156662306a36Sopenharmony_ci } else { 156762306a36Sopenharmony_ci pix->ycbcr_enc = src_data->format.ycbcr_enc; 156862306a36Sopenharmony_ci pix->quantization = src_data->format.quantization; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* 157262306a36Sopenharmony_ci * The frame width is identical to the output queue, and the height is 157362306a36Sopenharmony_ci * either doubled or identical depending on whether the output queue 157462306a36Sopenharmony_ci * field order contains one or two fields per frame. 157562306a36Sopenharmony_ci */ 157662306a36Sopenharmony_ci pix->width = src_data->format.width; 157762306a36Sopenharmony_ci if (src_data->format.field == V4L2_FIELD_ALTERNATE) 157862306a36Sopenharmony_ci pix->height = 2 * src_data->format.height; 157962306a36Sopenharmony_ci else 158062306a36Sopenharmony_ci pix->height = src_data->format.height; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci fdp1_compute_stride(pix, fmt); 158362306a36Sopenharmony_ci} 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cistatic int fdp1_try_fmt(struct file *file, void *priv, struct v4l2_format *f) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci struct fdp1_ctx *ctx = fh_to_ctx(priv); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 159062306a36Sopenharmony_ci fdp1_try_fmt_output(ctx, NULL, &f->fmt.pix_mp); 159162306a36Sopenharmony_ci else 159262306a36Sopenharmony_ci fdp1_try_fmt_capture(ctx, NULL, &f->fmt.pix_mp); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci dprintk(ctx->fdp1, "Try %s format: %4.4s (0x%08x) %ux%u field %u\n", 159562306a36Sopenharmony_ci V4L2_TYPE_IS_OUTPUT(f->type) ? "output" : "capture", 159662306a36Sopenharmony_ci (char *)&f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.pixelformat, 159762306a36Sopenharmony_ci f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.field); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return 0; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic void fdp1_set_format(struct fdp1_ctx *ctx, 160362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix, 160462306a36Sopenharmony_ci enum v4l2_buf_type type) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct fdp1_q_data *q_data = get_q_data(ctx, type); 160762306a36Sopenharmony_ci const struct fdp1_fmt *fmtinfo; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 161062306a36Sopenharmony_ci fdp1_try_fmt_output(ctx, &fmtinfo, pix); 161162306a36Sopenharmony_ci else 161262306a36Sopenharmony_ci fdp1_try_fmt_capture(ctx, &fmtinfo, pix); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci q_data->fmt = fmtinfo; 161562306a36Sopenharmony_ci q_data->format = *pix; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci q_data->vsize = pix->height; 161862306a36Sopenharmony_ci if (pix->field != V4L2_FIELD_NONE) 161962306a36Sopenharmony_ci q_data->vsize /= 2; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci q_data->stride_y = pix->plane_fmt[0].bytesperline; 162262306a36Sopenharmony_ci q_data->stride_c = pix->plane_fmt[1].bytesperline; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci /* Adjust strides for interleaved buffers */ 162562306a36Sopenharmony_ci if (pix->field == V4L2_FIELD_INTERLACED || 162662306a36Sopenharmony_ci pix->field == V4L2_FIELD_INTERLACED_TB || 162762306a36Sopenharmony_ci pix->field == V4L2_FIELD_INTERLACED_BT) { 162862306a36Sopenharmony_ci q_data->stride_y *= 2; 162962306a36Sopenharmony_ci q_data->stride_c *= 2; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* Propagate the format from the output node to the capture node. */ 163362306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 163462306a36Sopenharmony_ci struct fdp1_q_data *dst_data = &ctx->cap_q; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* 163762306a36Sopenharmony_ci * Copy the format, clear the per-plane bytes per line and image 163862306a36Sopenharmony_ci * size, override the field and double the height if needed. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci dst_data->format = q_data->format; 164162306a36Sopenharmony_ci memset(dst_data->format.plane_fmt, 0, 164262306a36Sopenharmony_ci sizeof(dst_data->format.plane_fmt)); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci dst_data->format.field = V4L2_FIELD_NONE; 164562306a36Sopenharmony_ci if (pix->field == V4L2_FIELD_ALTERNATE) 164662306a36Sopenharmony_ci dst_data->format.height *= 2; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci fdp1_try_fmt_capture(ctx, &dst_data->fmt, &dst_data->format); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci dst_data->vsize = dst_data->format.height; 165162306a36Sopenharmony_ci dst_data->stride_y = dst_data->format.plane_fmt[0].bytesperline; 165262306a36Sopenharmony_ci dst_data->stride_c = dst_data->format.plane_fmt[1].bytesperline; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic int fdp1_s_fmt(struct file *file, void *priv, struct v4l2_format *f) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci struct fdp1_ctx *ctx = fh_to_ctx(priv); 165962306a36Sopenharmony_ci struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; 166062306a36Sopenharmony_ci struct vb2_queue *vq = v4l2_m2m_get_vq(m2m_ctx, f->type); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 166362306a36Sopenharmony_ci v4l2_err(&ctx->fdp1->v4l2_dev, "%s queue busy\n", __func__); 166462306a36Sopenharmony_ci return -EBUSY; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci fdp1_set_format(ctx, &f->fmt.pix_mp, f->type); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci dprintk(ctx->fdp1, "Set %s format: %4.4s (0x%08x) %ux%u field %u\n", 167062306a36Sopenharmony_ci V4L2_TYPE_IS_OUTPUT(f->type) ? "output" : "capture", 167162306a36Sopenharmony_ci (char *)&f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.pixelformat, 167262306a36Sopenharmony_ci f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.field); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci return 0; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int fdp1_g_ctrl(struct v4l2_ctrl *ctrl) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci struct fdp1_ctx *ctx = 168062306a36Sopenharmony_ci container_of(ctrl->handler, struct fdp1_ctx, hdl); 168162306a36Sopenharmony_ci struct fdp1_q_data *src_q_data = &ctx->out_q; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci switch (ctrl->id) { 168462306a36Sopenharmony_ci case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: 168562306a36Sopenharmony_ci if (V4L2_FIELD_HAS_BOTH(src_q_data->format.field)) 168662306a36Sopenharmony_ci ctrl->val = 2; 168762306a36Sopenharmony_ci else 168862306a36Sopenharmony_ci ctrl->val = 1; 168962306a36Sopenharmony_ci return 0; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci return 1; 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int fdp1_s_ctrl(struct v4l2_ctrl *ctrl) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci struct fdp1_ctx *ctx = 169862306a36Sopenharmony_ci container_of(ctrl->handler, struct fdp1_ctx, hdl); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci switch (ctrl->id) { 170162306a36Sopenharmony_ci case V4L2_CID_ALPHA_COMPONENT: 170262306a36Sopenharmony_ci ctx->alpha = ctrl->val; 170362306a36Sopenharmony_ci break; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci case V4L2_CID_DEINTERLACING_MODE: 170662306a36Sopenharmony_ci ctx->deint_mode = ctrl->val; 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci return 0; 171162306a36Sopenharmony_ci} 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops fdp1_ctrl_ops = { 171462306a36Sopenharmony_ci .s_ctrl = fdp1_s_ctrl, 171562306a36Sopenharmony_ci .g_volatile_ctrl = fdp1_g_ctrl, 171662306a36Sopenharmony_ci}; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic const char * const fdp1_ctrl_deint_menu[] = { 171962306a36Sopenharmony_ci "Progressive", 172062306a36Sopenharmony_ci "Adaptive 2D/3D", 172162306a36Sopenharmony_ci "Fixed 2D", 172262306a36Sopenharmony_ci "Fixed 3D", 172362306a36Sopenharmony_ci "Previous field", 172462306a36Sopenharmony_ci "Next field", 172562306a36Sopenharmony_ci NULL 172662306a36Sopenharmony_ci}; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops fdp1_ioctl_ops = { 172962306a36Sopenharmony_ci .vidioc_querycap = fdp1_vidioc_querycap, 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = fdp1_enum_fmt_vid_cap, 173262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = fdp1_enum_fmt_vid_out, 173362306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = fdp1_g_fmt, 173462306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = fdp1_g_fmt, 173562306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = fdp1_try_fmt, 173662306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = fdp1_try_fmt, 173762306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = fdp1_s_fmt, 173862306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = fdp1_s_fmt, 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 174162306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 174262306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 174362306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 174462306a36Sopenharmony_ci .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 174562306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 174662306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 174962306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 175262306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 175362306a36Sopenharmony_ci}; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci/* 175662306a36Sopenharmony_ci * Queue operations 175762306a36Sopenharmony_ci */ 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int fdp1_queue_setup(struct vb2_queue *vq, 176062306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 176162306a36Sopenharmony_ci unsigned int sizes[], 176262306a36Sopenharmony_ci struct device *alloc_ctxs[]) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci struct fdp1_ctx *ctx = vb2_get_drv_priv(vq); 176562306a36Sopenharmony_ci struct fdp1_q_data *q_data; 176662306a36Sopenharmony_ci unsigned int i; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci q_data = get_q_data(ctx, vq->type); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (*nplanes) { 177162306a36Sopenharmony_ci if (*nplanes > FDP1_MAX_PLANES) 177262306a36Sopenharmony_ci return -EINVAL; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci return 0; 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci *nplanes = q_data->format.num_planes; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci for (i = 0; i < *nplanes; i++) 178062306a36Sopenharmony_ci sizes[i] = q_data->format.plane_fmt[i].sizeimage; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci return 0; 178362306a36Sopenharmony_ci} 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_cistatic void fdp1_buf_prepare_field(struct fdp1_q_data *q_data, 178662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf, 178762306a36Sopenharmony_ci unsigned int field_num) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci struct fdp1_buffer *buf = to_fdp1_buffer(vbuf); 179062306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf = &buf->fields[field_num]; 179162306a36Sopenharmony_ci unsigned int num_fields; 179262306a36Sopenharmony_ci unsigned int i; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci fbuf->vb = vbuf; 179762306a36Sopenharmony_ci fbuf->last_field = (field_num + 1) == num_fields; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci for (i = 0; i < vbuf->vb2_buf.num_planes; ++i) 180062306a36Sopenharmony_ci fbuf->addrs[i] = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, i); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci switch (vbuf->field) { 180362306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 180462306a36Sopenharmony_ci /* 180562306a36Sopenharmony_ci * Interlaced means bottom-top for 60Hz TV standards (NTSC) and 180662306a36Sopenharmony_ci * top-bottom for 50Hz. As TV standards are not applicable to 180762306a36Sopenharmony_ci * the mem-to-mem API, use the height as a heuristic. 180862306a36Sopenharmony_ci */ 180962306a36Sopenharmony_ci fbuf->field = (q_data->format.height < 576) == field_num 181062306a36Sopenharmony_ci ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; 181162306a36Sopenharmony_ci break; 181262306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 181362306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 181462306a36Sopenharmony_ci fbuf->field = field_num ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; 181562306a36Sopenharmony_ci break; 181662306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 181762306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 181862306a36Sopenharmony_ci fbuf->field = field_num ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; 181962306a36Sopenharmony_ci break; 182062306a36Sopenharmony_ci default: 182162306a36Sopenharmony_ci fbuf->field = vbuf->field; 182262306a36Sopenharmony_ci break; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci /* Buffer is completed */ 182662306a36Sopenharmony_ci if (!field_num) 182762306a36Sopenharmony_ci return; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci /* Adjust buffer addresses for second field */ 183062306a36Sopenharmony_ci switch (vbuf->field) { 183162306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 183262306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 183362306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 183462306a36Sopenharmony_ci for (i = 0; i < vbuf->vb2_buf.num_planes; i++) 183562306a36Sopenharmony_ci fbuf->addrs[i] += 183662306a36Sopenharmony_ci (i == 0 ? q_data->stride_y : q_data->stride_c); 183762306a36Sopenharmony_ci break; 183862306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 183962306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 184062306a36Sopenharmony_ci for (i = 0; i < vbuf->vb2_buf.num_planes; i++) 184162306a36Sopenharmony_ci fbuf->addrs[i] += q_data->vsize * 184262306a36Sopenharmony_ci (i == 0 ? q_data->stride_y : q_data->stride_c); 184362306a36Sopenharmony_ci break; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci} 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_cistatic int fdp1_buf_prepare(struct vb2_buffer *vb) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct fdp1_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 185062306a36Sopenharmony_ci struct fdp1_q_data *q_data = get_q_data(ctx, vb->vb2_queue->type); 185162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 185262306a36Sopenharmony_ci struct fdp1_buffer *buf = to_fdp1_buffer(vbuf); 185362306a36Sopenharmony_ci unsigned int i; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { 185662306a36Sopenharmony_ci bool field_valid = true; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* Validate the buffer field. */ 185962306a36Sopenharmony_ci switch (q_data->format.field) { 186062306a36Sopenharmony_ci case V4L2_FIELD_NONE: 186162306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_NONE) 186262306a36Sopenharmony_ci field_valid = false; 186362306a36Sopenharmony_ci break; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci case V4L2_FIELD_ALTERNATE: 186662306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_TOP && 186762306a36Sopenharmony_ci vbuf->field != V4L2_FIELD_BOTTOM) 186862306a36Sopenharmony_ci field_valid = false; 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 187262306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 187362306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 187462306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 187562306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 187662306a36Sopenharmony_ci if (vbuf->field != q_data->format.field) 187762306a36Sopenharmony_ci field_valid = false; 187862306a36Sopenharmony_ci break; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (!field_valid) { 188262306a36Sopenharmony_ci dprintk(ctx->fdp1, 188362306a36Sopenharmony_ci "buffer field %u invalid for format field %u\n", 188462306a36Sopenharmony_ci vbuf->field, q_data->format.field); 188562306a36Sopenharmony_ci return -EINVAL; 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci } else { 188862306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci /* Validate the planes sizes. */ 189262306a36Sopenharmony_ci for (i = 0; i < q_data->format.num_planes; i++) { 189362306a36Sopenharmony_ci unsigned long size = q_data->format.plane_fmt[i].sizeimage; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < size) { 189662306a36Sopenharmony_ci dprintk(ctx->fdp1, 189762306a36Sopenharmony_ci "data will not fit into plane [%u/%u] (%lu < %lu)\n", 189862306a36Sopenharmony_ci i, q_data->format.num_planes, 189962306a36Sopenharmony_ci vb2_plane_size(vb, i), size); 190062306a36Sopenharmony_ci return -EINVAL; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci /* We have known size formats all around */ 190462306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, size); 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci buf->num_fields = V4L2_FIELD_HAS_BOTH(vbuf->field) ? 2 : 1; 190862306a36Sopenharmony_ci for (i = 0; i < buf->num_fields; ++i) 190962306a36Sopenharmony_ci fdp1_buf_prepare_field(q_data, vbuf, i); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci return 0; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic void fdp1_buf_queue(struct vb2_buffer *vb) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 191762306a36Sopenharmony_ci struct fdp1_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 192062306a36Sopenharmony_ci} 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic int fdp1_start_streaming(struct vb2_queue *q, unsigned int count) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci struct fdp1_ctx *ctx = vb2_get_drv_priv(q); 192562306a36Sopenharmony_ci struct fdp1_q_data *q_data = get_q_data(ctx, q->type); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) { 192862306a36Sopenharmony_ci /* 192962306a36Sopenharmony_ci * Force our deint_mode when we are progressive, 193062306a36Sopenharmony_ci * ignoring any setting on the device from the user, 193162306a36Sopenharmony_ci * Otherwise, lock in the requested de-interlace mode. 193262306a36Sopenharmony_ci */ 193362306a36Sopenharmony_ci if (q_data->format.field == V4L2_FIELD_NONE) 193462306a36Sopenharmony_ci ctx->deint_mode = FDP1_PROGRESSIVE; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (ctx->deint_mode == FDP1_ADAPT2D3D) { 193762306a36Sopenharmony_ci u32 stride; 193862306a36Sopenharmony_ci dma_addr_t smsk_base; 193962306a36Sopenharmony_ci const u32 bpp = 2; /* bytes per pixel */ 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci stride = round_up(q_data->format.width, 8); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci ctx->smsk_size = bpp * stride * q_data->vsize; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci ctx->smsk_cpu = dma_alloc_coherent(ctx->fdp1->dev, 194662306a36Sopenharmony_ci ctx->smsk_size, &smsk_base, GFP_KERNEL); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci if (ctx->smsk_cpu == NULL) { 194962306a36Sopenharmony_ci dprintk(ctx->fdp1, "Failed to alloc smsk\n"); 195062306a36Sopenharmony_ci return -ENOMEM; 195162306a36Sopenharmony_ci } 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci ctx->smsk_addr[0] = smsk_base; 195462306a36Sopenharmony_ci ctx->smsk_addr[1] = smsk_base + (ctx->smsk_size/2); 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic void fdp1_stop_streaming(struct vb2_queue *q) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci struct fdp1_ctx *ctx = vb2_get_drv_priv(q); 196462306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 196562306a36Sopenharmony_ci unsigned long flags; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci while (1) { 196862306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) 196962306a36Sopenharmony_ci vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 197062306a36Sopenharmony_ci else 197162306a36Sopenharmony_ci vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 197262306a36Sopenharmony_ci if (vbuf == NULL) 197362306a36Sopenharmony_ci break; 197462306a36Sopenharmony_ci spin_lock_irqsave(&ctx->fdp1->irqlock, flags); 197562306a36Sopenharmony_ci v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); 197662306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->fdp1->irqlock, flags); 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Empty Output queues */ 198062306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) { 198162306a36Sopenharmony_ci /* Empty our internal queues */ 198262306a36Sopenharmony_ci struct fdp1_field_buffer *fbuf; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* Free any queued buffers */ 198562306a36Sopenharmony_ci fbuf = fdp1_dequeue_field(ctx); 198662306a36Sopenharmony_ci while (fbuf != NULL) { 198762306a36Sopenharmony_ci fdp1_field_complete(ctx, fbuf); 198862306a36Sopenharmony_ci fbuf = fdp1_dequeue_field(ctx); 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* Free smsk_data */ 199262306a36Sopenharmony_ci if (ctx->smsk_cpu) { 199362306a36Sopenharmony_ci dma_free_coherent(ctx->fdp1->dev, ctx->smsk_size, 199462306a36Sopenharmony_ci ctx->smsk_cpu, ctx->smsk_addr[0]); 199562306a36Sopenharmony_ci ctx->smsk_addr[0] = ctx->smsk_addr[1] = 0; 199662306a36Sopenharmony_ci ctx->smsk_cpu = NULL; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci WARN(!list_empty(&ctx->fields_queue), 200062306a36Sopenharmony_ci "Buffer queue not empty"); 200162306a36Sopenharmony_ci } else { 200262306a36Sopenharmony_ci /* Empty Capture queues (Jobs) */ 200362306a36Sopenharmony_ci struct fdp1_job *job; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci job = get_queued_job(ctx->fdp1); 200662306a36Sopenharmony_ci while (job) { 200762306a36Sopenharmony_ci if (FDP1_DEINT_MODE_USES_PREV(ctx->deint_mode)) 200862306a36Sopenharmony_ci fdp1_field_complete(ctx, job->previous); 200962306a36Sopenharmony_ci else 201062306a36Sopenharmony_ci fdp1_field_complete(ctx, job->active); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci v4l2_m2m_buf_done(job->dst->vb, VB2_BUF_STATE_ERROR); 201362306a36Sopenharmony_ci job->dst = NULL; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci job = get_queued_job(ctx->fdp1); 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci /* Free any held buffer in the ctx */ 201962306a36Sopenharmony_ci fdp1_field_complete(ctx, ctx->previous); 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci WARN(!list_empty(&ctx->fdp1->queued_job_list), 202262306a36Sopenharmony_ci "Queued Job List not empty"); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci WARN(!list_empty(&ctx->fdp1->hw_job_list), 202562306a36Sopenharmony_ci "HW Job list not empty"); 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci} 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_cistatic const struct vb2_ops fdp1_qops = { 203062306a36Sopenharmony_ci .queue_setup = fdp1_queue_setup, 203162306a36Sopenharmony_ci .buf_prepare = fdp1_buf_prepare, 203262306a36Sopenharmony_ci .buf_queue = fdp1_buf_queue, 203362306a36Sopenharmony_ci .start_streaming = fdp1_start_streaming, 203462306a36Sopenharmony_ci .stop_streaming = fdp1_stop_streaming, 203562306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 203662306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 203762306a36Sopenharmony_ci}; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 204062306a36Sopenharmony_ci struct vb2_queue *dst_vq) 204162306a36Sopenharmony_ci{ 204262306a36Sopenharmony_ci struct fdp1_ctx *ctx = priv; 204362306a36Sopenharmony_ci int ret; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 204662306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 204762306a36Sopenharmony_ci src_vq->drv_priv = ctx; 204862306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct fdp1_buffer); 204962306a36Sopenharmony_ci src_vq->ops = &fdp1_qops; 205062306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 205162306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 205262306a36Sopenharmony_ci src_vq->lock = &ctx->fdp1->dev_mutex; 205362306a36Sopenharmony_ci src_vq->dev = ctx->fdp1->dev; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 205662306a36Sopenharmony_ci if (ret) 205762306a36Sopenharmony_ci return ret; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 206062306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 206162306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 206262306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct fdp1_buffer); 206362306a36Sopenharmony_ci dst_vq->ops = &fdp1_qops; 206462306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 206562306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 206662306a36Sopenharmony_ci dst_vq->lock = &ctx->fdp1->dev_mutex; 206762306a36Sopenharmony_ci dst_vq->dev = ctx->fdp1->dev; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci/* 207362306a36Sopenharmony_ci * File operations 207462306a36Sopenharmony_ci */ 207562306a36Sopenharmony_cistatic int fdp1_open(struct file *file) 207662306a36Sopenharmony_ci{ 207762306a36Sopenharmony_ci struct fdp1_dev *fdp1 = video_drvdata(file); 207862306a36Sopenharmony_ci struct v4l2_pix_format_mplane format; 207962306a36Sopenharmony_ci struct fdp1_ctx *ctx = NULL; 208062306a36Sopenharmony_ci struct v4l2_ctrl *ctrl; 208162306a36Sopenharmony_ci int ret = 0; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci if (mutex_lock_interruptible(&fdp1->dev_mutex)) 208462306a36Sopenharmony_ci return -ERESTARTSYS; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 208762306a36Sopenharmony_ci if (!ctx) { 208862306a36Sopenharmony_ci ret = -ENOMEM; 208962306a36Sopenharmony_ci goto done; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 209362306a36Sopenharmony_ci file->private_data = &ctx->fh; 209462306a36Sopenharmony_ci ctx->fdp1 = fdp1; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci /* Initialise Queues */ 209762306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->fields_queue); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci ctx->translen = 1; 210062306a36Sopenharmony_ci ctx->sequence = 0; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* Initialise controls */ 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->hdl, 3); 210562306a36Sopenharmony_ci v4l2_ctrl_new_std_menu_items(&ctx->hdl, &fdp1_ctrl_ops, 210662306a36Sopenharmony_ci V4L2_CID_DEINTERLACING_MODE, 210762306a36Sopenharmony_ci FDP1_NEXTFIELD, BIT(0), FDP1_FIXED3D, 210862306a36Sopenharmony_ci fdp1_ctrl_deint_menu); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&ctx->hdl, &fdp1_ctrl_ops, 211162306a36Sopenharmony_ci V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 2, 1, 1); 211262306a36Sopenharmony_ci if (ctrl) 211362306a36Sopenharmony_ci ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->hdl, &fdp1_ctrl_ops, 211662306a36Sopenharmony_ci V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci if (ctx->hdl.error) { 211962306a36Sopenharmony_ci ret = ctx->hdl.error; 212062306a36Sopenharmony_ci goto error_ctx; 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->hdl; 212462306a36Sopenharmony_ci v4l2_ctrl_handler_setup(&ctx->hdl); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* Configure default parameters. */ 212762306a36Sopenharmony_ci memset(&format, 0, sizeof(format)); 212862306a36Sopenharmony_ci fdp1_set_format(ctx, &format, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fdp1->m2m_dev, ctx, &queue_init); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 213362306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 213462306a36Sopenharmony_ci goto error_ctx; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci /* Perform any power management required */ 213862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(fdp1->dev); 213962306a36Sopenharmony_ci if (ret < 0) 214062306a36Sopenharmony_ci goto error_pm; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci dprintk(fdp1, "Created instance: %p, m2m_ctx: %p\n", 214562306a36Sopenharmony_ci ctx, ctx->fh.m2m_ctx); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci mutex_unlock(&fdp1->dev_mutex); 214862306a36Sopenharmony_ci return 0; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cierror_pm: 215162306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 215262306a36Sopenharmony_cierror_ctx: 215362306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 215462306a36Sopenharmony_ci kfree(ctx); 215562306a36Sopenharmony_cidone: 215662306a36Sopenharmony_ci mutex_unlock(&fdp1->dev_mutex); 215762306a36Sopenharmony_ci return ret; 215862306a36Sopenharmony_ci} 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_cistatic int fdp1_release(struct file *file) 216162306a36Sopenharmony_ci{ 216262306a36Sopenharmony_ci struct fdp1_dev *fdp1 = video_drvdata(file); 216362306a36Sopenharmony_ci struct fdp1_ctx *ctx = fh_to_ctx(file->private_data); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci dprintk(fdp1, "Releasing instance %p\n", ctx); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 216862306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 216962306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 217062306a36Sopenharmony_ci mutex_lock(&fdp1->dev_mutex); 217162306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 217262306a36Sopenharmony_ci mutex_unlock(&fdp1->dev_mutex); 217362306a36Sopenharmony_ci kfree(ctx); 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci pm_runtime_put(fdp1->dev); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci return 0; 217862306a36Sopenharmony_ci} 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_cistatic const struct v4l2_file_operations fdp1_fops = { 218162306a36Sopenharmony_ci .owner = THIS_MODULE, 218262306a36Sopenharmony_ci .open = fdp1_open, 218362306a36Sopenharmony_ci .release = fdp1_release, 218462306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 218562306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 218662306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 218762306a36Sopenharmony_ci}; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_cistatic const struct video_device fdp1_videodev = { 219062306a36Sopenharmony_ci .name = DRIVER_NAME, 219162306a36Sopenharmony_ci .vfl_dir = VFL_DIR_M2M, 219262306a36Sopenharmony_ci .fops = &fdp1_fops, 219362306a36Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, 219462306a36Sopenharmony_ci .ioctl_ops = &fdp1_ioctl_ops, 219562306a36Sopenharmony_ci .minor = -1, 219662306a36Sopenharmony_ci .release = video_device_release_empty, 219762306a36Sopenharmony_ci}; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_cistatic const struct v4l2_m2m_ops m2m_ops = { 220062306a36Sopenharmony_ci .device_run = fdp1_m2m_device_run, 220162306a36Sopenharmony_ci .job_ready = fdp1_m2m_job_ready, 220262306a36Sopenharmony_ci .job_abort = fdp1_m2m_job_abort, 220362306a36Sopenharmony_ci}; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_cistatic irqreturn_t fdp1_irq_handler(int irq, void *dev_id) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci struct fdp1_dev *fdp1 = dev_id; 220862306a36Sopenharmony_ci u32 int_status; 220962306a36Sopenharmony_ci u32 ctl_status; 221062306a36Sopenharmony_ci u32 vint_cnt; 221162306a36Sopenharmony_ci u32 cycles; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci int_status = fdp1_read(fdp1, FD1_CTL_IRQSTA); 221462306a36Sopenharmony_ci cycles = fdp1_read(fdp1, FD1_CTL_VCYCLE_STAT); 221562306a36Sopenharmony_ci ctl_status = fdp1_read(fdp1, FD1_CTL_STATUS); 221662306a36Sopenharmony_ci vint_cnt = (ctl_status & FD1_CTL_STATUS_VINT_CNT_MASK) >> 221762306a36Sopenharmony_ci FD1_CTL_STATUS_VINT_CNT_SHIFT; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* Clear interrupts */ 222062306a36Sopenharmony_ci fdp1_write(fdp1, ~(int_status) & FD1_CTL_IRQ_MASK, FD1_CTL_IRQSTA); 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci if (debug >= 2) { 222362306a36Sopenharmony_ci dprintk(fdp1, "IRQ: 0x%x %s%s%s\n", int_status, 222462306a36Sopenharmony_ci int_status & FD1_CTL_IRQ_VERE ? "[Error]" : "[!E]", 222562306a36Sopenharmony_ci int_status & FD1_CTL_IRQ_VINTE ? "[VSync]" : "[!V]", 222662306a36Sopenharmony_ci int_status & FD1_CTL_IRQ_FREE ? "[FrameEnd]" : "[!F]"); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci dprintk(fdp1, "CycleStatus = %d (%dms)\n", 222962306a36Sopenharmony_ci cycles, cycles/(fdp1->clk_rate/1000)); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci dprintk(fdp1, 223262306a36Sopenharmony_ci "Control Status = 0x%08x : VINT_CNT = %d %s:%s:%s:%s\n", 223362306a36Sopenharmony_ci ctl_status, vint_cnt, 223462306a36Sopenharmony_ci ctl_status & FD1_CTL_STATUS_SGREGSET ? "RegSet" : "", 223562306a36Sopenharmony_ci ctl_status & FD1_CTL_STATUS_SGVERR ? "Vsync Error" : "", 223662306a36Sopenharmony_ci ctl_status & FD1_CTL_STATUS_SGFREND ? "FrameEnd" : "", 223762306a36Sopenharmony_ci ctl_status & FD1_CTL_STATUS_BSY ? "Busy" : ""); 223862306a36Sopenharmony_ci dprintk(fdp1, "***********************************\n"); 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci /* Spurious interrupt */ 224262306a36Sopenharmony_ci if (!(FD1_CTL_IRQ_MASK & int_status)) 224362306a36Sopenharmony_ci return IRQ_NONE; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci /* Work completed, release the frame */ 224662306a36Sopenharmony_ci if (FD1_CTL_IRQ_VERE & int_status) 224762306a36Sopenharmony_ci device_frame_end(fdp1, VB2_BUF_STATE_ERROR); 224862306a36Sopenharmony_ci else if (FD1_CTL_IRQ_FREE & int_status) 224962306a36Sopenharmony_ci device_frame_end(fdp1, VB2_BUF_STATE_DONE); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci return IRQ_HANDLED; 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_cistatic int fdp1_probe(struct platform_device *pdev) 225562306a36Sopenharmony_ci{ 225662306a36Sopenharmony_ci struct fdp1_dev *fdp1; 225762306a36Sopenharmony_ci struct video_device *vfd; 225862306a36Sopenharmony_ci struct device_node *fcp_node; 225962306a36Sopenharmony_ci struct clk *clk; 226062306a36Sopenharmony_ci unsigned int i; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci int ret; 226362306a36Sopenharmony_ci int hw_version; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci fdp1 = devm_kzalloc(&pdev->dev, sizeof(*fdp1), GFP_KERNEL); 226662306a36Sopenharmony_ci if (!fdp1) 226762306a36Sopenharmony_ci return -ENOMEM; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci INIT_LIST_HEAD(&fdp1->free_job_list); 227062306a36Sopenharmony_ci INIT_LIST_HEAD(&fdp1->queued_job_list); 227162306a36Sopenharmony_ci INIT_LIST_HEAD(&fdp1->hw_job_list); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci /* Initialise the jobs on the free list */ 227462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fdp1->jobs); i++) 227562306a36Sopenharmony_ci list_add(&fdp1->jobs[i].list, &fdp1->free_job_list); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci mutex_init(&fdp1->dev_mutex); 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci spin_lock_init(&fdp1->irqlock); 228062306a36Sopenharmony_ci spin_lock_init(&fdp1->device_process_lock); 228162306a36Sopenharmony_ci fdp1->dev = &pdev->dev; 228262306a36Sopenharmony_ci platform_set_drvdata(pdev, fdp1); 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci /* Memory-mapped registers */ 228562306a36Sopenharmony_ci fdp1->regs = devm_platform_ioremap_resource(pdev, 0); 228662306a36Sopenharmony_ci if (IS_ERR(fdp1->regs)) 228762306a36Sopenharmony_ci return PTR_ERR(fdp1->regs); 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci /* Interrupt service routine registration */ 229062306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 229162306a36Sopenharmony_ci if (ret < 0) 229262306a36Sopenharmony_ci return ret; 229362306a36Sopenharmony_ci fdp1->irq = ret; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, fdp1->irq, fdp1_irq_handler, 0, 229662306a36Sopenharmony_ci dev_name(&pdev->dev), fdp1); 229762306a36Sopenharmony_ci if (ret) { 229862306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot claim IRQ %d\n", fdp1->irq); 229962306a36Sopenharmony_ci return ret; 230062306a36Sopenharmony_ci } 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci /* FCP */ 230362306a36Sopenharmony_ci fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); 230462306a36Sopenharmony_ci if (fcp_node) { 230562306a36Sopenharmony_ci fdp1->fcp = rcar_fcp_get(fcp_node); 230662306a36Sopenharmony_ci of_node_put(fcp_node); 230762306a36Sopenharmony_ci if (IS_ERR(fdp1->fcp)) { 230862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "FCP not found (%ld)\n", 230962306a36Sopenharmony_ci PTR_ERR(fdp1->fcp)); 231062306a36Sopenharmony_ci return PTR_ERR(fdp1->fcp); 231162306a36Sopenharmony_ci } 231262306a36Sopenharmony_ci } 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci /* Determine our clock rate */ 231562306a36Sopenharmony_ci clk = clk_get(&pdev->dev, NULL); 231662306a36Sopenharmony_ci if (IS_ERR(clk)) { 231762306a36Sopenharmony_ci ret = PTR_ERR(clk); 231862306a36Sopenharmony_ci goto put_dev; 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci fdp1->clk_rate = clk_get_rate(clk); 232262306a36Sopenharmony_ci clk_put(clk); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci /* V4L2 device registration */ 232562306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &fdp1->v4l2_dev); 232662306a36Sopenharmony_ci if (ret) { 232762306a36Sopenharmony_ci v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n"); 232862306a36Sopenharmony_ci goto put_dev; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci /* M2M registration */ 233262306a36Sopenharmony_ci fdp1->m2m_dev = v4l2_m2m_init(&m2m_ops); 233362306a36Sopenharmony_ci if (IS_ERR(fdp1->m2m_dev)) { 233462306a36Sopenharmony_ci v4l2_err(&fdp1->v4l2_dev, "Failed to init mem2mem device\n"); 233562306a36Sopenharmony_ci ret = PTR_ERR(fdp1->m2m_dev); 233662306a36Sopenharmony_ci goto unreg_dev; 233762306a36Sopenharmony_ci } 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci /* Video registration */ 234062306a36Sopenharmony_ci fdp1->vfd = fdp1_videodev; 234162306a36Sopenharmony_ci vfd = &fdp1->vfd; 234262306a36Sopenharmony_ci vfd->lock = &fdp1->dev_mutex; 234362306a36Sopenharmony_ci vfd->v4l2_dev = &fdp1->v4l2_dev; 234462306a36Sopenharmony_ci video_set_drvdata(vfd, fdp1); 234562306a36Sopenharmony_ci strscpy(vfd->name, fdp1_videodev.name, sizeof(vfd->name)); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 234862306a36Sopenharmony_ci if (ret) { 234962306a36Sopenharmony_ci v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n"); 235062306a36Sopenharmony_ci goto release_m2m; 235162306a36Sopenharmony_ci } 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci v4l2_info(&fdp1->v4l2_dev, "Device registered as /dev/video%d\n", 235462306a36Sopenharmony_ci vfd->num); 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci /* Power up the cells to read HW */ 235762306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 235862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(fdp1->dev); 235962306a36Sopenharmony_ci if (ret < 0) 236062306a36Sopenharmony_ci goto disable_pm; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci hw_version = fdp1_read(fdp1, FD1_IP_INTDATA); 236362306a36Sopenharmony_ci switch (hw_version) { 236462306a36Sopenharmony_ci case FD1_IP_GEN2: 236562306a36Sopenharmony_ci dprintk(fdp1, "FDP1 Version R-Car Gen2\n"); 236662306a36Sopenharmony_ci break; 236762306a36Sopenharmony_ci case FD1_IP_M3W: 236862306a36Sopenharmony_ci dprintk(fdp1, "FDP1 Version R-Car M3-W\n"); 236962306a36Sopenharmony_ci break; 237062306a36Sopenharmony_ci case FD1_IP_H3: 237162306a36Sopenharmony_ci dprintk(fdp1, "FDP1 Version R-Car H3\n"); 237262306a36Sopenharmony_ci break; 237362306a36Sopenharmony_ci case FD1_IP_M3N: 237462306a36Sopenharmony_ci dprintk(fdp1, "FDP1 Version R-Car M3-N\n"); 237562306a36Sopenharmony_ci break; 237662306a36Sopenharmony_ci case FD1_IP_E3: 237762306a36Sopenharmony_ci dprintk(fdp1, "FDP1 Version R-Car E3\n"); 237862306a36Sopenharmony_ci break; 237962306a36Sopenharmony_ci default: 238062306a36Sopenharmony_ci dev_err(fdp1->dev, "FDP1 Unidentifiable (0x%08x)\n", 238162306a36Sopenharmony_ci hw_version); 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci /* Allow the hw to sleep until an open call puts it to use */ 238562306a36Sopenharmony_ci pm_runtime_put(fdp1->dev); 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci return 0; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_cidisable_pm: 239062306a36Sopenharmony_ci pm_runtime_disable(fdp1->dev); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_cirelease_m2m: 239362306a36Sopenharmony_ci v4l2_m2m_release(fdp1->m2m_dev); 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ciunreg_dev: 239662306a36Sopenharmony_ci v4l2_device_unregister(&fdp1->v4l2_dev); 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ciput_dev: 239962306a36Sopenharmony_ci rcar_fcp_put(fdp1->fcp); 240062306a36Sopenharmony_ci return ret; 240162306a36Sopenharmony_ci} 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_cistatic void fdp1_remove(struct platform_device *pdev) 240462306a36Sopenharmony_ci{ 240562306a36Sopenharmony_ci struct fdp1_dev *fdp1 = platform_get_drvdata(pdev); 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci v4l2_m2m_release(fdp1->m2m_dev); 240862306a36Sopenharmony_ci video_unregister_device(&fdp1->vfd); 240962306a36Sopenharmony_ci v4l2_device_unregister(&fdp1->v4l2_dev); 241062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 241162306a36Sopenharmony_ci rcar_fcp_put(fdp1->fcp); 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_cistatic int __maybe_unused fdp1_pm_runtime_suspend(struct device *dev) 241562306a36Sopenharmony_ci{ 241662306a36Sopenharmony_ci struct fdp1_dev *fdp1 = dev_get_drvdata(dev); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci rcar_fcp_disable(fdp1->fcp); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci return 0; 242162306a36Sopenharmony_ci} 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_cistatic int __maybe_unused fdp1_pm_runtime_resume(struct device *dev) 242462306a36Sopenharmony_ci{ 242562306a36Sopenharmony_ci struct fdp1_dev *fdp1 = dev_get_drvdata(dev); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci /* Program in the static LUTs */ 242862306a36Sopenharmony_ci fdp1_set_lut(fdp1); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci return rcar_fcp_enable(fdp1->fcp); 243162306a36Sopenharmony_ci} 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_cistatic const struct dev_pm_ops fdp1_pm_ops = { 243462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(fdp1_pm_runtime_suspend, 243562306a36Sopenharmony_ci fdp1_pm_runtime_resume, 243662306a36Sopenharmony_ci NULL) 243762306a36Sopenharmony_ci}; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_cistatic const struct of_device_id fdp1_dt_ids[] = { 244062306a36Sopenharmony_ci { .compatible = "renesas,fdp1" }, 244162306a36Sopenharmony_ci { }, 244262306a36Sopenharmony_ci}; 244362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fdp1_dt_ids); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_cistatic struct platform_driver fdp1_pdrv = { 244662306a36Sopenharmony_ci .probe = fdp1_probe, 244762306a36Sopenharmony_ci .remove_new = fdp1_remove, 244862306a36Sopenharmony_ci .driver = { 244962306a36Sopenharmony_ci .name = DRIVER_NAME, 245062306a36Sopenharmony_ci .of_match_table = fdp1_dt_ids, 245162306a36Sopenharmony_ci .pm = &fdp1_pm_ops, 245262306a36Sopenharmony_ci }, 245362306a36Sopenharmony_ci}; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_cimodule_platform_driver(fdp1_pdrv); 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas R-Car Fine Display Processor Driver"); 245862306a36Sopenharmony_ciMODULE_AUTHOR("Kieran Bingham <kieran@bingham.xyz>"); 245962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 246062306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 2461