162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012-2014 Mentor Graphics Inc. 462306a36Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/export.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/videodev2.h> 1562306a36Sopenharmony_ci#include <uapi/linux/v4l2-mediabus.h> 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/clk-provider.h> 1862306a36Sopenharmony_ci#include <linux/clkdev.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "ipu-prv.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct ipu_csi { 2362306a36Sopenharmony_ci void __iomem *base; 2462306a36Sopenharmony_ci int id; 2562306a36Sopenharmony_ci u32 module; 2662306a36Sopenharmony_ci struct clk *clk_ipu; /* IPU bus clock */ 2762306a36Sopenharmony_ci spinlock_t lock; 2862306a36Sopenharmony_ci bool inuse; 2962306a36Sopenharmony_ci struct ipu_soc *ipu; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* CSI Register Offsets */ 3362306a36Sopenharmony_ci#define CSI_SENS_CONF 0x0000 3462306a36Sopenharmony_ci#define CSI_SENS_FRM_SIZE 0x0004 3562306a36Sopenharmony_ci#define CSI_ACT_FRM_SIZE 0x0008 3662306a36Sopenharmony_ci#define CSI_OUT_FRM_CTRL 0x000c 3762306a36Sopenharmony_ci#define CSI_TST_CTRL 0x0010 3862306a36Sopenharmony_ci#define CSI_CCIR_CODE_1 0x0014 3962306a36Sopenharmony_ci#define CSI_CCIR_CODE_2 0x0018 4062306a36Sopenharmony_ci#define CSI_CCIR_CODE_3 0x001c 4162306a36Sopenharmony_ci#define CSI_MIPI_DI 0x0020 4262306a36Sopenharmony_ci#define CSI_SKIP 0x0024 4362306a36Sopenharmony_ci#define CSI_CPD_CTRL 0x0028 4462306a36Sopenharmony_ci#define CSI_CPD_RC(n) (0x002c + ((n)*4)) 4562306a36Sopenharmony_ci#define CSI_CPD_RS(n) (0x004c + ((n)*4)) 4662306a36Sopenharmony_ci#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) 4762306a36Sopenharmony_ci#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) 4862306a36Sopenharmony_ci#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) 4962306a36Sopenharmony_ci#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) 5062306a36Sopenharmony_ci#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) 5162306a36Sopenharmony_ci#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) 5262306a36Sopenharmony_ci#define CSI_CPD_OFFSET1 0x00ec 5362306a36Sopenharmony_ci#define CSI_CPD_OFFSET2 0x00f0 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* CSI Register Fields */ 5662306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 5762306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 5862306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L 5962306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L 6062306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L 6162306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_BAYER 3L 6262306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB565 4L 6362306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB555 5L 6462306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB444 6L 6562306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_JPEG 7L 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 6862306a36Sopenharmony_ci#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 6962306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_POL_SHIFT 2 7062306a36Sopenharmony_ci#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 7162306a36Sopenharmony_ci#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 7262306a36Sopenharmony_ci#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 7362306a36Sopenharmony_ci#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 7462306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 7562306a36Sopenharmony_ci#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 7662306a36Sopenharmony_ci#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 7962306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 8062306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 8162306a36Sopenharmony_ci#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 8262306a36Sopenharmony_ci#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 8362306a36Sopenharmony_ci#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 8462306a36Sopenharmony_ci#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define CSI_DATA_DEST_IC 2 8762306a36Sopenharmony_ci#define CSI_DATA_DEST_IDMAC 4 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define CSI_CCIR_ERR_DET_EN 0x01000000 9062306a36Sopenharmony_ci#define CSI_HORI_DOWNSIZE_EN 0x80000000 9162306a36Sopenharmony_ci#define CSI_VERT_DOWNSIZE_EN 0x40000000 9262306a36Sopenharmony_ci#define CSI_TEST_GEN_MODE_EN 0x01000000 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define CSI_HSC_MASK 0x1fff0000 9562306a36Sopenharmony_ci#define CSI_HSC_SHIFT 16 9662306a36Sopenharmony_ci#define CSI_VSC_MASK 0x00000fff 9762306a36Sopenharmony_ci#define CSI_VSC_SHIFT 0 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define CSI_TEST_GEN_R_MASK 0x000000ff 10062306a36Sopenharmony_ci#define CSI_TEST_GEN_R_SHIFT 0 10162306a36Sopenharmony_ci#define CSI_TEST_GEN_G_MASK 0x0000ff00 10262306a36Sopenharmony_ci#define CSI_TEST_GEN_G_SHIFT 8 10362306a36Sopenharmony_ci#define CSI_TEST_GEN_B_MASK 0x00ff0000 10462306a36Sopenharmony_ci#define CSI_TEST_GEN_B_SHIFT 16 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 10762306a36Sopenharmony_ci#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 10862306a36Sopenharmony_ci#define CSI_SKIP_SMFC_MASK 0x000000f8 10962306a36Sopenharmony_ci#define CSI_SKIP_SMFC_SHIFT 3 11062306a36Sopenharmony_ci#define CSI_ID_2_SKIP_MASK 0x00000300 11162306a36Sopenharmony_ci#define CSI_ID_2_SKIP_SHIFT 8 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 11462306a36Sopenharmony_ci#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* MIPI CSI-2 data types */ 11762306a36Sopenharmony_ci#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ 11862306a36Sopenharmony_ci#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ 11962306a36Sopenharmony_ci#define MIPI_DT_YUV422 0x1e /* UYVY... */ 12062306a36Sopenharmony_ci#define MIPI_DT_RGB444 0x20 12162306a36Sopenharmony_ci#define MIPI_DT_RGB555 0x21 12262306a36Sopenharmony_ci#define MIPI_DT_RGB565 0x22 12362306a36Sopenharmony_ci#define MIPI_DT_RGB666 0x23 12462306a36Sopenharmony_ci#define MIPI_DT_RGB888 0x24 12562306a36Sopenharmony_ci#define MIPI_DT_RAW6 0x28 12662306a36Sopenharmony_ci#define MIPI_DT_RAW7 0x29 12762306a36Sopenharmony_ci#define MIPI_DT_RAW8 0x2a 12862306a36Sopenharmony_ci#define MIPI_DT_RAW10 0x2b 12962306a36Sopenharmony_ci#define MIPI_DT_RAW12 0x2c 13062306a36Sopenharmony_ci#define MIPI_DT_RAW14 0x2d 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Bitfield of CSI bus signal polarities and modes. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistruct ipu_csi_bus_config { 13662306a36Sopenharmony_ci unsigned data_width:4; 13762306a36Sopenharmony_ci unsigned clk_mode:3; 13862306a36Sopenharmony_ci unsigned ext_vsync:1; 13962306a36Sopenharmony_ci unsigned vsync_pol:1; 14062306a36Sopenharmony_ci unsigned hsync_pol:1; 14162306a36Sopenharmony_ci unsigned pixclk_pol:1; 14262306a36Sopenharmony_ci unsigned data_pol:1; 14362306a36Sopenharmony_ci unsigned sens_clksrc:1; 14462306a36Sopenharmony_ci unsigned pack_tight:1; 14562306a36Sopenharmony_ci unsigned force_eof:1; 14662306a36Sopenharmony_ci unsigned data_en_pol:1; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci unsigned data_fmt; 14962306a36Sopenharmony_ci unsigned mipi_dt; 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* 15362306a36Sopenharmony_ci * Enumeration of CSI data bus widths. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cienum ipu_csi_data_width { 15662306a36Sopenharmony_ci IPU_CSI_DATA_WIDTH_4 = 0, 15762306a36Sopenharmony_ci IPU_CSI_DATA_WIDTH_8 = 1, 15862306a36Sopenharmony_ci IPU_CSI_DATA_WIDTH_10 = 3, 15962306a36Sopenharmony_ci IPU_CSI_DATA_WIDTH_12 = 5, 16062306a36Sopenharmony_ci IPU_CSI_DATA_WIDTH_16 = 9, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Enumeration of CSI clock modes. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cienum ipu_csi_clk_mode { 16762306a36Sopenharmony_ci IPU_CSI_CLK_MODE_GATED_CLK, 16862306a36Sopenharmony_ci IPU_CSI_CLK_MODE_NONGATED_CLK, 16962306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, 17062306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_INTERLACED, 17162306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, 17262306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, 17362306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, 17462306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci return readl(csi->base + offset); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic inline void ipu_csi_write(struct ipu_csi *csi, u32 value, 18362306a36Sopenharmony_ci unsigned offset) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci writel(value, csi->base + offset); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * Set mclk division ratio for generating test mode mclk. Only used 19062306a36Sopenharmony_ci * for test generator. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, 19362306a36Sopenharmony_ci u32 ipu_clk) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 temp; 19662306a36Sopenharmony_ci int div_ratio; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci div_ratio = (ipu_clk / pixel_clk) - 1; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (div_ratio > 0xFF || div_ratio < 0) { 20162306a36Sopenharmony_ci dev_err(csi->ipu->dev, 20262306a36Sopenharmony_ci "value of pixel_clk extends normal range\n"); 20362306a36Sopenharmony_ci return -EINVAL; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci temp = ipu_csi_read(csi, CSI_SENS_CONF); 20762306a36Sopenharmony_ci temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; 20862306a36Sopenharmony_ci ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), 20962306a36Sopenharmony_ci CSI_SENS_CONF); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * Find the CSI data format and data width for the given V4L2 media 21662306a36Sopenharmony_ci * bus pixel format code. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, 21962306a36Sopenharmony_ci enum v4l2_mbus_type mbus_type) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci switch (mbus_code) { 22262306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_BE: 22362306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_LE: 22462306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_BE: 22562306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 22662306a36Sopenharmony_ci if (mbus_type == V4L2_MBUS_CSI2_DPHY) 22762306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; 22862306a36Sopenharmony_ci else 22962306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 23062306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB565; 23162306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: 23462306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: 23562306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; 23662306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB444; 23762306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: 24062306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 24162306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; 24262306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB555; 24362306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 24662306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR888_1X24: 24762306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; 24862306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB888; 24962306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 25262306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; 25362306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 25462306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 25762306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; 25862306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 25962306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 26262306a36Sopenharmony_ci if (mbus_type == V4L2_MBUS_BT656) { 26362306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; 26462306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 26762306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_16; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 27262306a36Sopenharmony_ci if (mbus_type == V4L2_MBUS_BT656) { 27362306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; 27462306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 27762306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_16; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR8_1X8: 28262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG8_1X8: 28362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG8_1X8: 28462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB8_1X8: 28562306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y8_1X8: 28662306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 28762306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW8; 28862306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: 29162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: 29262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: 29362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: 29462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: 29562306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: 29662306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: 29762306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: 29862306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 29962306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW10; 30062306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_1X10: 30362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_1X10: 30462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_1X10: 30562306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_1X10: 30662306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y10_1X10: 30762306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 30862306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW10; 30962306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_10; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR12_1X12: 31262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG12_1X12: 31362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG12_1X12: 31462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB12_1X12: 31562306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y12_1X12: 31662306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 31762306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW12; 31862306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_12; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case MEDIA_BUS_FMT_JPEG_1X8: 32162306a36Sopenharmony_ci /* TODO */ 32262306a36Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; 32362306a36Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW8; 32462306a36Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci default: 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* translate alternate field mode based on given standard */ 33462306a36Sopenharmony_cistatic inline enum v4l2_field 33562306a36Sopenharmony_ciipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci return (field != V4L2_FIELD_ALTERNATE) ? field : 33862306a36Sopenharmony_ci ((std & V4L2_STD_525_60) ? 33962306a36Sopenharmony_ci V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/* 34362306a36Sopenharmony_ci * Fill a CSI bus config struct from mbus_config and mbus_framefmt. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, 34662306a36Sopenharmony_ci const struct v4l2_mbus_config *mbus_cfg, 34762306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *mbus_fmt) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci int ret, is_bt1120; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci memset(csicfg, 0, sizeof(*csicfg)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); 35462306a36Sopenharmony_ci if (ret < 0) 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (mbus_cfg->type) { 35862306a36Sopenharmony_ci case V4L2_MBUS_PARALLEL: 35962306a36Sopenharmony_ci csicfg->ext_vsync = 1; 36062306a36Sopenharmony_ci csicfg->vsync_pol = (mbus_cfg->bus.parallel.flags & 36162306a36Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; 36262306a36Sopenharmony_ci csicfg->hsync_pol = (mbus_cfg->bus.parallel.flags & 36362306a36Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; 36462306a36Sopenharmony_ci csicfg->pixclk_pol = (mbus_cfg->bus.parallel.flags & 36562306a36Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; 36662306a36Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case V4L2_MBUS_BT656: 36962306a36Sopenharmony_ci csicfg->ext_vsync = 0; 37062306a36Sopenharmony_ci /* UYVY10_1X20 etc. should be supported as well */ 37162306a36Sopenharmony_ci is_bt1120 = mbus_fmt->code == MEDIA_BUS_FMT_UYVY8_1X16 || 37262306a36Sopenharmony_ci mbus_fmt->code == MEDIA_BUS_FMT_YUYV8_1X16; 37362306a36Sopenharmony_ci if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || 37462306a36Sopenharmony_ci mbus_fmt->field == V4L2_FIELD_ALTERNATE) 37562306a36Sopenharmony_ci csicfg->clk_mode = is_bt1120 ? 37662306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR : 37762306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_INTERLACED; 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci csicfg->clk_mode = is_bt1120 ? 38062306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR : 38162306a36Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * MIPI CSI-2 requires non gated clock mode, all other 38662306a36Sopenharmony_ci * parameters are not applicable for MIPI CSI-2 bus. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci default: 39162306a36Sopenharmony_ci /* will never get here, keep compiler quiet */ 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int 39962306a36Sopenharmony_ciipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, 40062306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *infmt, 40162306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *outfmt, 40262306a36Sopenharmony_ci v4l2_std_id std) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci enum v4l2_field infield, outfield; 40562306a36Sopenharmony_ci bool swap_fields; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* get translated field type of input and output */ 40862306a36Sopenharmony_ci infield = ipu_csi_translate_field(infmt->field, std); 40962306a36Sopenharmony_ci outfield = ipu_csi_translate_field(outfmt->field, std); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * Write the H-V-F codes the CSI will match against the 41362306a36Sopenharmony_ci * incoming data for start/end of active and blanking 41462306a36Sopenharmony_ci * field intervals. If input and output field types are 41562306a36Sopenharmony_ci * sequential but not the same (one is SEQ_BT and the other 41662306a36Sopenharmony_ci * is SEQ_TB), swap the F-bit so that the CSI will capture 41762306a36Sopenharmony_ci * field 1 lines before field 0 lines. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && 42062306a36Sopenharmony_ci V4L2_FIELD_IS_SEQUENTIAL(outfield) && 42162306a36Sopenharmony_ci infield != outfield); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!swap_fields) { 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * Field0BlankEnd = 110, Field0BlankStart = 010 42662306a36Sopenharmony_ci * Field0ActiveEnd = 100, Field0ActiveStart = 000 42762306a36Sopenharmony_ci * Field1BlankEnd = 111, Field1BlankStart = 011 42862306a36Sopenharmony_ci * Field1ActiveEnd = 101, Field1ActiveStart = 001 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, 43162306a36Sopenharmony_ci CSI_CCIR_CODE_1); 43262306a36Sopenharmony_ci ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "capture field swap\n"); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* same as above but with F-bit inverted */ 43762306a36Sopenharmony_ci ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, 43862306a36Sopenharmony_ci CSI_CCIR_CODE_1); 43962306a36Sopenharmony_ci ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ciint ipu_csi_init_interface(struct ipu_csi *csi, 44962306a36Sopenharmony_ci const struct v4l2_mbus_config *mbus_cfg, 45062306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *infmt, 45162306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *outfmt) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct ipu_csi_bus_config cfg; 45462306a36Sopenharmony_ci unsigned long flags; 45562306a36Sopenharmony_ci u32 width, height, data = 0; 45662306a36Sopenharmony_ci v4l2_std_id std; 45762306a36Sopenharmony_ci int ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); 46062306a36Sopenharmony_ci if (ret < 0) 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* set default sensor frame width and height */ 46462306a36Sopenharmony_ci width = infmt->width; 46562306a36Sopenharmony_ci height = infmt->height; 46662306a36Sopenharmony_ci if (infmt->field == V4L2_FIELD_ALTERNATE) 46762306a36Sopenharmony_ci height *= 2; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Set the CSI_SENS_CONF register remaining fields */ 47062306a36Sopenharmony_ci data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | 47162306a36Sopenharmony_ci cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | 47262306a36Sopenharmony_ci cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | 47362306a36Sopenharmony_ci cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | 47462306a36Sopenharmony_ci cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | 47562306a36Sopenharmony_ci cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | 47662306a36Sopenharmony_ci cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | 47762306a36Sopenharmony_ci cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | 47862306a36Sopenharmony_ci cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | 47962306a36Sopenharmony_ci cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | 48062306a36Sopenharmony_ci cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ipu_csi_write(csi, data, CSI_SENS_CONF); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Set CCIR registers */ 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci switch (cfg.clk_mode) { 48962306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: 49062306a36Sopenharmony_ci ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); 49162306a36Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: 49462306a36Sopenharmony_ci if (width == 720 && height == 480) { 49562306a36Sopenharmony_ci std = V4L2_STD_NTSC; 49662306a36Sopenharmony_ci height = 525; 49762306a36Sopenharmony_ci } else if (width == 720 && height == 576) { 49862306a36Sopenharmony_ci std = V4L2_STD_PAL; 49962306a36Sopenharmony_ci height = 625; 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci dev_err(csi->ipu->dev, 50262306a36Sopenharmony_ci "Unsupported interlaced video mode\n"); 50362306a36Sopenharmony_ci ret = -EINVAL; 50462306a36Sopenharmony_ci goto out_unlock; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci goto out_unlock; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: 51262306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: 51362306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: 51462306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: 51562306a36Sopenharmony_ci ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, 51662306a36Sopenharmony_ci CSI_CCIR_CODE_1); 51762306a36Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_GATED_CLK: 52062306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_NONGATED_CLK: 52162306a36Sopenharmony_ci ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Setup sensor frame size */ 52662306a36Sopenharmony_ci ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), 52762306a36Sopenharmony_ci CSI_SENS_FRM_SIZE); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", 53062306a36Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_CONF)); 53162306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", 53262306a36Sopenharmony_ci ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciout_unlock: 53562306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_init_interface); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cibool ipu_csi_is_interlaced(struct ipu_csi *csi) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci unsigned long flags; 54462306a36Sopenharmony_ci u32 sensor_protocol; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 54762306a36Sopenharmony_ci sensor_protocol = 54862306a36Sopenharmony_ci (ipu_csi_read(csi, CSI_SENS_CONF) & 54962306a36Sopenharmony_ci CSI_SENS_CONF_SENS_PRTCL_MASK) >> 55062306a36Sopenharmony_ci CSI_SENS_CONF_SENS_PRTCL_SHIFT; 55162306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci switch (sensor_protocol) { 55462306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_GATED_CLK: 55562306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_NONGATED_CLK: 55662306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: 55762306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: 55862306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: 55962306a36Sopenharmony_ci return false; 56062306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: 56162306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: 56262306a36Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: 56362306a36Sopenharmony_ci return true; 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci dev_err(csi->ipu->dev, 56662306a36Sopenharmony_ci "CSI %d sensor protocol unsupported\n", csi->id); 56762306a36Sopenharmony_ci return false; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_civoid ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci unsigned long flags; 57562306a36Sopenharmony_ci u32 reg; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); 58062306a36Sopenharmony_ci w->width = (reg & 0xFFFF) + 1; 58162306a36Sopenharmony_ci w->height = (reg >> 16 & 0xFFFF) + 1; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 58462306a36Sopenharmony_ci w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; 58562306a36Sopenharmony_ci w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_get_window); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_civoid ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci unsigned long flags; 59462306a36Sopenharmony_ci u32 reg; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), 59962306a36Sopenharmony_ci CSI_ACT_FRM_SIZE); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 60262306a36Sopenharmony_ci reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); 60362306a36Sopenharmony_ci reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); 60462306a36Sopenharmony_ci ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_window); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_civoid ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci unsigned long flags; 61362306a36Sopenharmony_ci u32 reg; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 61862306a36Sopenharmony_ci reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); 61962306a36Sopenharmony_ci reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | 62062306a36Sopenharmony_ci (vert ? CSI_VERT_DOWNSIZE_EN : 0); 62162306a36Sopenharmony_ci ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_downsize); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_civoid ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, 62862306a36Sopenharmony_ci u32 r_value, u32 g_value, u32 b_value, 62962306a36Sopenharmony_ci u32 pix_clk) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci unsigned long flags; 63262306a36Sopenharmony_ci u32 ipu_clk = clk_get_rate(csi->clk_ipu); 63362306a36Sopenharmony_ci u32 temp; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci temp = ipu_csi_read(csi, CSI_TST_CTRL); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (!active) { 64062306a36Sopenharmony_ci temp &= ~CSI_TEST_GEN_MODE_EN; 64162306a36Sopenharmony_ci ipu_csi_write(csi, temp, CSI_TST_CTRL); 64262306a36Sopenharmony_ci } else { 64362306a36Sopenharmony_ci /* Set sensb_mclk div_ratio */ 64462306a36Sopenharmony_ci ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | 64762306a36Sopenharmony_ci CSI_TEST_GEN_B_MASK); 64862306a36Sopenharmony_ci temp |= CSI_TEST_GEN_MODE_EN; 64962306a36Sopenharmony_ci temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | 65062306a36Sopenharmony_ci (g_value << CSI_TEST_GEN_G_SHIFT) | 65162306a36Sopenharmony_ci (b_value << CSI_TEST_GEN_B_SHIFT); 65262306a36Sopenharmony_ci ipu_csi_write(csi, temp, CSI_TST_CTRL); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ciint ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, 66062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct ipu_csi_bus_config cfg; 66362306a36Sopenharmony_ci unsigned long flags; 66462306a36Sopenharmony_ci u32 temp; 66562306a36Sopenharmony_ci int ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (vc > 3) 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); 67162306a36Sopenharmony_ci if (ret < 0) 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci temp = ipu_csi_read(csi, CSI_MIPI_DI); 67762306a36Sopenharmony_ci temp &= ~(0xff << (vc * 8)); 67862306a36Sopenharmony_ci temp |= (cfg.mipi_dt << (vc * 8)); 67962306a36Sopenharmony_ci ipu_csi_write(csi, temp, CSI_MIPI_DI); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciint ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, 68862306a36Sopenharmony_ci u32 max_ratio, u32 id) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci unsigned long flags; 69162306a36Sopenharmony_ci u32 temp; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (max_ratio > 5 || id > 3) 69462306a36Sopenharmony_ci return -EINVAL; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci temp = ipu_csi_read(csi, CSI_SKIP); 69962306a36Sopenharmony_ci temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | 70062306a36Sopenharmony_ci CSI_SKIP_SMFC_MASK); 70162306a36Sopenharmony_ci temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | 70262306a36Sopenharmony_ci (id << CSI_ID_2_SKIP_SHIFT) | 70362306a36Sopenharmony_ci (skip << CSI_SKIP_SMFC_SHIFT); 70462306a36Sopenharmony_ci ipu_csi_write(csi, temp, CSI_SKIP); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ciint ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci unsigned long flags; 71562306a36Sopenharmony_ci u32 csi_sens_conf, dest; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (csi_dest == IPU_CSI_DEST_IDMAC) 71862306a36Sopenharmony_ci dest = CSI_DATA_DEST_IDMAC; 71962306a36Sopenharmony_ci else 72062306a36Sopenharmony_ci dest = CSI_DATA_DEST_IC; /* IC or VDIC */ 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); 72562306a36Sopenharmony_ci csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; 72662306a36Sopenharmony_ci csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); 72762306a36Sopenharmony_ci ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_dest); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciint ipu_csi_enable(struct ipu_csi *csi) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci ipu_module_enable(csi->ipu, csi->module); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_enable); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciint ipu_csi_disable(struct ipu_csi *csi) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci ipu_module_disable(csi->ipu, csi->module); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_disable); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistruct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci unsigned long flags; 75462306a36Sopenharmony_ci struct ipu_csi *csi, *ret; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (id > 1) 75762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci csi = ipu->csi_priv[id]; 76062306a36Sopenharmony_ci ret = csi; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (csi->inuse) { 76562306a36Sopenharmony_ci ret = ERR_PTR(-EBUSY); 76662306a36Sopenharmony_ci goto unlock; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci csi->inuse = true; 77062306a36Sopenharmony_ciunlock: 77162306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_get); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_civoid ipu_csi_put(struct ipu_csi *csi) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci unsigned long flags; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 78162306a36Sopenharmony_ci csi->inuse = false; 78262306a36Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_put); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ciint ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, 78762306a36Sopenharmony_ci unsigned long base, u32 module, struct clk *clk_ipu) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct ipu_csi *csi; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (id > 1) 79262306a36Sopenharmony_ci return -ENODEV; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); 79562306a36Sopenharmony_ci if (!csi) 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ipu->csi_priv[id] = csi; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci spin_lock_init(&csi->lock); 80162306a36Sopenharmony_ci csi->module = module; 80262306a36Sopenharmony_ci csi->id = id; 80362306a36Sopenharmony_ci csi->clk_ipu = clk_ipu; 80462306a36Sopenharmony_ci csi->base = devm_ioremap(dev, base, PAGE_SIZE); 80562306a36Sopenharmony_ci if (!csi->base) 80662306a36Sopenharmony_ci return -ENOMEM; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", 80962306a36Sopenharmony_ci id, base, csi->base); 81062306a36Sopenharmony_ci csi->ipu = ipu; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_civoid ipu_csi_exit(struct ipu_soc *ipu, int id) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_civoid ipu_csi_dump(struct ipu_csi *csi) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", 82262306a36Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_CONF)); 82362306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", 82462306a36Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); 82562306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", 82662306a36Sopenharmony_ci ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); 82762306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", 82862306a36Sopenharmony_ci ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); 82962306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", 83062306a36Sopenharmony_ci ipu_csi_read(csi, CSI_TST_CTRL)); 83162306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", 83262306a36Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_1)); 83362306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", 83462306a36Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_2)); 83562306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", 83662306a36Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_3)); 83762306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", 83862306a36Sopenharmony_ci ipu_csi_read(csi, CSI_MIPI_DI)); 83962306a36Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", 84062306a36Sopenharmony_ci ipu_csi_read(csi, CSI_SKIP)); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_dump); 843