18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2014 Mentor Graphics Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 158c2ecf20Sopenharmony_ci#include <uapi/linux/v4l2-mediabus.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 188c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "ipu-prv.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct ipu_csi { 238c2ecf20Sopenharmony_ci void __iomem *base; 248c2ecf20Sopenharmony_ci int id; 258c2ecf20Sopenharmony_ci u32 module; 268c2ecf20Sopenharmony_ci struct clk *clk_ipu; /* IPU bus clock */ 278c2ecf20Sopenharmony_ci spinlock_t lock; 288c2ecf20Sopenharmony_ci bool inuse; 298c2ecf20Sopenharmony_ci struct ipu_soc *ipu; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* CSI Register Offsets */ 338c2ecf20Sopenharmony_ci#define CSI_SENS_CONF 0x0000 348c2ecf20Sopenharmony_ci#define CSI_SENS_FRM_SIZE 0x0004 358c2ecf20Sopenharmony_ci#define CSI_ACT_FRM_SIZE 0x0008 368c2ecf20Sopenharmony_ci#define CSI_OUT_FRM_CTRL 0x000c 378c2ecf20Sopenharmony_ci#define CSI_TST_CTRL 0x0010 388c2ecf20Sopenharmony_ci#define CSI_CCIR_CODE_1 0x0014 398c2ecf20Sopenharmony_ci#define CSI_CCIR_CODE_2 0x0018 408c2ecf20Sopenharmony_ci#define CSI_CCIR_CODE_3 0x001c 418c2ecf20Sopenharmony_ci#define CSI_MIPI_DI 0x0020 428c2ecf20Sopenharmony_ci#define CSI_SKIP 0x0024 438c2ecf20Sopenharmony_ci#define CSI_CPD_CTRL 0x0028 448c2ecf20Sopenharmony_ci#define CSI_CPD_RC(n) (0x002c + ((n)*4)) 458c2ecf20Sopenharmony_ci#define CSI_CPD_RS(n) (0x004c + ((n)*4)) 468c2ecf20Sopenharmony_ci#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) 478c2ecf20Sopenharmony_ci#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) 488c2ecf20Sopenharmony_ci#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) 498c2ecf20Sopenharmony_ci#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) 508c2ecf20Sopenharmony_ci#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) 518c2ecf20Sopenharmony_ci#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) 528c2ecf20Sopenharmony_ci#define CSI_CPD_OFFSET1 0x00ec 538c2ecf20Sopenharmony_ci#define CSI_CPD_OFFSET2 0x00f0 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* CSI Register Fields */ 568c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 578c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 588c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L 598c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L 608c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L 618c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_BAYER 3L 628c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB565 4L 638c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB555 5L 648c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_RGB444 6L 658c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_FMT_JPEG 7L 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 688c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 698c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_POL_SHIFT 2 708c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 718c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 728c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 738c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 748c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 758c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 768c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 798c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 808c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 818c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 828c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 838c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 848c2ecf20Sopenharmony_ci#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define CSI_DATA_DEST_IC 2 878c2ecf20Sopenharmony_ci#define CSI_DATA_DEST_IDMAC 4 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define CSI_CCIR_ERR_DET_EN 0x01000000 908c2ecf20Sopenharmony_ci#define CSI_HORI_DOWNSIZE_EN 0x80000000 918c2ecf20Sopenharmony_ci#define CSI_VERT_DOWNSIZE_EN 0x40000000 928c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_MODE_EN 0x01000000 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define CSI_HSC_MASK 0x1fff0000 958c2ecf20Sopenharmony_ci#define CSI_HSC_SHIFT 16 968c2ecf20Sopenharmony_ci#define CSI_VSC_MASK 0x00000fff 978c2ecf20Sopenharmony_ci#define CSI_VSC_SHIFT 0 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_R_MASK 0x000000ff 1008c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_R_SHIFT 0 1018c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_G_MASK 0x0000ff00 1028c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_G_SHIFT 8 1038c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_B_MASK 0x00ff0000 1048c2ecf20Sopenharmony_ci#define CSI_TEST_GEN_B_SHIFT 16 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 1078c2ecf20Sopenharmony_ci#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 1088c2ecf20Sopenharmony_ci#define CSI_SKIP_SMFC_MASK 0x000000f8 1098c2ecf20Sopenharmony_ci#define CSI_SKIP_SMFC_SHIFT 3 1108c2ecf20Sopenharmony_ci#define CSI_ID_2_SKIP_MASK 0x00000300 1118c2ecf20Sopenharmony_ci#define CSI_ID_2_SKIP_SHIFT 8 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 1148c2ecf20Sopenharmony_ci#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* MIPI CSI-2 data types */ 1178c2ecf20Sopenharmony_ci#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ 1188c2ecf20Sopenharmony_ci#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ 1198c2ecf20Sopenharmony_ci#define MIPI_DT_YUV422 0x1e /* UYVY... */ 1208c2ecf20Sopenharmony_ci#define MIPI_DT_RGB444 0x20 1218c2ecf20Sopenharmony_ci#define MIPI_DT_RGB555 0x21 1228c2ecf20Sopenharmony_ci#define MIPI_DT_RGB565 0x22 1238c2ecf20Sopenharmony_ci#define MIPI_DT_RGB666 0x23 1248c2ecf20Sopenharmony_ci#define MIPI_DT_RGB888 0x24 1258c2ecf20Sopenharmony_ci#define MIPI_DT_RAW6 0x28 1268c2ecf20Sopenharmony_ci#define MIPI_DT_RAW7 0x29 1278c2ecf20Sopenharmony_ci#define MIPI_DT_RAW8 0x2a 1288c2ecf20Sopenharmony_ci#define MIPI_DT_RAW10 0x2b 1298c2ecf20Sopenharmony_ci#define MIPI_DT_RAW12 0x2c 1308c2ecf20Sopenharmony_ci#define MIPI_DT_RAW14 0x2d 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* 1338c2ecf20Sopenharmony_ci * Bitfield of CSI bus signal polarities and modes. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistruct ipu_csi_bus_config { 1368c2ecf20Sopenharmony_ci unsigned data_width:4; 1378c2ecf20Sopenharmony_ci unsigned clk_mode:3; 1388c2ecf20Sopenharmony_ci unsigned ext_vsync:1; 1398c2ecf20Sopenharmony_ci unsigned vsync_pol:1; 1408c2ecf20Sopenharmony_ci unsigned hsync_pol:1; 1418c2ecf20Sopenharmony_ci unsigned pixclk_pol:1; 1428c2ecf20Sopenharmony_ci unsigned data_pol:1; 1438c2ecf20Sopenharmony_ci unsigned sens_clksrc:1; 1448c2ecf20Sopenharmony_ci unsigned pack_tight:1; 1458c2ecf20Sopenharmony_ci unsigned force_eof:1; 1468c2ecf20Sopenharmony_ci unsigned data_en_pol:1; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci unsigned data_fmt; 1498c2ecf20Sopenharmony_ci unsigned mipi_dt; 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * Enumeration of CSI data bus widths. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cienum ipu_csi_data_width { 1568c2ecf20Sopenharmony_ci IPU_CSI_DATA_WIDTH_4 = 0, 1578c2ecf20Sopenharmony_ci IPU_CSI_DATA_WIDTH_8 = 1, 1588c2ecf20Sopenharmony_ci IPU_CSI_DATA_WIDTH_10 = 3, 1598c2ecf20Sopenharmony_ci IPU_CSI_DATA_WIDTH_12 = 5, 1608c2ecf20Sopenharmony_ci IPU_CSI_DATA_WIDTH_16 = 9, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * Enumeration of CSI clock modes. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cienum ipu_csi_clk_mode { 1678c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_GATED_CLK, 1688c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_NONGATED_CLK, 1698c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, 1708c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR656_INTERLACED, 1718c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, 1728c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, 1738c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, 1748c2ecf20Sopenharmony_ci IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return readl(csi->base + offset); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic inline void ipu_csi_write(struct ipu_csi *csi, u32 value, 1838c2ecf20Sopenharmony_ci unsigned offset) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci writel(value, csi->base + offset); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * Set mclk division ratio for generating test mode mclk. Only used 1908c2ecf20Sopenharmony_ci * for test generator. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, 1938c2ecf20Sopenharmony_ci u32 ipu_clk) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci u32 temp; 1968c2ecf20Sopenharmony_ci int div_ratio; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci div_ratio = (ipu_clk / pixel_clk) - 1; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (div_ratio > 0xFF || div_ratio < 0) { 2018c2ecf20Sopenharmony_ci dev_err(csi->ipu->dev, 2028c2ecf20Sopenharmony_ci "value of pixel_clk extends normal range\n"); 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci temp = ipu_csi_read(csi, CSI_SENS_CONF); 2078c2ecf20Sopenharmony_ci temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; 2088c2ecf20Sopenharmony_ci ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), 2098c2ecf20Sopenharmony_ci CSI_SENS_CONF); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * Find the CSI data format and data width for the given V4L2 media 2168c2ecf20Sopenharmony_ci * bus pixel format code. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, 2198c2ecf20Sopenharmony_ci enum v4l2_mbus_type mbus_type) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci switch (mbus_code) { 2228c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_BE: 2238c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_LE: 2248c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_BE: 2258c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 2268c2ecf20Sopenharmony_ci if (mbus_type == V4L2_MBUS_CSI2_DPHY) 2278c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 2308c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB565; 2318c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: 2348c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: 2358c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; 2368c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB444; 2378c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: 2408c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 2418c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; 2428c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB555; 2438c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 2468c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_BGR888_1X24: 2478c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; 2488c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RGB888; 2498c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 2528c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; 2538c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 2548c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 2578c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; 2588c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 2598c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 2628c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 2638c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 2648c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_YUV422; 2658c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_16; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR8_1X8: 2688c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG8_1X8: 2698c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG8_1X8: 2708c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB8_1X8: 2718c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_Y8_1X8: 2728c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 2738c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW8; 2748c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: 2778c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: 2788c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: 2798c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: 2808c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: 2818c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: 2828c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: 2838c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: 2848c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 2858c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW10; 2868c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_1X10: 2898c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_1X10: 2908c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_1X10: 2918c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_1X10: 2928c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_Y10_1X10: 2938c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 2948c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW10; 2958c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_10; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR12_1X12: 2988c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG12_1X12: 2998c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG12_1X12: 3008c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB12_1X12: 3018c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_Y12_1X12: 3028c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; 3038c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW12; 3048c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_12; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_JPEG_1X8: 3078c2ecf20Sopenharmony_ci /* TODO */ 3088c2ecf20Sopenharmony_ci cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; 3098c2ecf20Sopenharmony_ci cfg->mipi_dt = MIPI_DT_RAW8; 3108c2ecf20Sopenharmony_ci cfg->data_width = IPU_CSI_DATA_WIDTH_8; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* translate alternate field mode based on given standard */ 3208c2ecf20Sopenharmony_cistatic inline enum v4l2_field 3218c2ecf20Sopenharmony_ciipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci return (field != V4L2_FIELD_ALTERNATE) ? field : 3248c2ecf20Sopenharmony_ci ((std & V4L2_STD_525_60) ? 3258c2ecf20Sopenharmony_ci V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * Fill a CSI bus config struct from mbus_config and mbus_framefmt. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, 3328c2ecf20Sopenharmony_ci const struct v4l2_mbus_config *mbus_cfg, 3338c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *mbus_fmt) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci memset(csicfg, 0, sizeof(*csicfg)); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); 3408c2ecf20Sopenharmony_ci if (ret < 0) 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci switch (mbus_cfg->type) { 3448c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 3458c2ecf20Sopenharmony_ci csicfg->ext_vsync = 1; 3468c2ecf20Sopenharmony_ci csicfg->vsync_pol = (mbus_cfg->flags & 3478c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; 3488c2ecf20Sopenharmony_ci csicfg->hsync_pol = (mbus_cfg->flags & 3498c2ecf20Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; 3508c2ecf20Sopenharmony_ci csicfg->pixclk_pol = (mbus_cfg->flags & 3518c2ecf20Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; 3528c2ecf20Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case V4L2_MBUS_BT656: 3558c2ecf20Sopenharmony_ci csicfg->ext_vsync = 0; 3568c2ecf20Sopenharmony_ci if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || 3578c2ecf20Sopenharmony_ci mbus_fmt->field == V4L2_FIELD_ALTERNATE) 3588c2ecf20Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * MIPI CSI-2 requires non gated clock mode, all other 3658c2ecf20Sopenharmony_ci * parameters are not applicable for MIPI CSI-2 bus. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci default: 3708c2ecf20Sopenharmony_ci /* will never get here, keep compiler quiet */ 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int 3788c2ecf20Sopenharmony_ciipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, 3798c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *infmt, 3808c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *outfmt, 3818c2ecf20Sopenharmony_ci v4l2_std_id std) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci enum v4l2_field infield, outfield; 3848c2ecf20Sopenharmony_ci bool swap_fields; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* get translated field type of input and output */ 3878c2ecf20Sopenharmony_ci infield = ipu_csi_translate_field(infmt->field, std); 3888c2ecf20Sopenharmony_ci outfield = ipu_csi_translate_field(outfmt->field, std); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Write the H-V-F codes the CSI will match against the 3928c2ecf20Sopenharmony_ci * incoming data for start/end of active and blanking 3938c2ecf20Sopenharmony_ci * field intervals. If input and output field types are 3948c2ecf20Sopenharmony_ci * sequential but not the same (one is SEQ_BT and the other 3958c2ecf20Sopenharmony_ci * is SEQ_TB), swap the F-bit so that the CSI will capture 3968c2ecf20Sopenharmony_ci * field 1 lines before field 0 lines. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && 3998c2ecf20Sopenharmony_ci V4L2_FIELD_IS_SEQUENTIAL(outfield) && 4008c2ecf20Sopenharmony_ci infield != outfield); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!swap_fields) { 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * Field0BlankEnd = 110, Field0BlankStart = 010 4058c2ecf20Sopenharmony_ci * Field0ActiveEnd = 100, Field0ActiveStart = 000 4068c2ecf20Sopenharmony_ci * Field1BlankEnd = 111, Field1BlankStart = 011 4078c2ecf20Sopenharmony_ci * Field1ActiveEnd = 101, Field1ActiveStart = 001 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, 4108c2ecf20Sopenharmony_ci CSI_CCIR_CODE_1); 4118c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); 4128c2ecf20Sopenharmony_ci } else { 4138c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "capture field swap\n"); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* same as above but with F-bit inverted */ 4168c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, 4178c2ecf20Sopenharmony_ci CSI_CCIR_CODE_1); 4188c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciint ipu_csi_init_interface(struct ipu_csi *csi, 4288c2ecf20Sopenharmony_ci const struct v4l2_mbus_config *mbus_cfg, 4298c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *infmt, 4308c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *outfmt) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct ipu_csi_bus_config cfg; 4338c2ecf20Sopenharmony_ci unsigned long flags; 4348c2ecf20Sopenharmony_ci u32 width, height, data = 0; 4358c2ecf20Sopenharmony_ci v4l2_std_id std; 4368c2ecf20Sopenharmony_ci int ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); 4398c2ecf20Sopenharmony_ci if (ret < 0) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* set default sensor frame width and height */ 4438c2ecf20Sopenharmony_ci width = infmt->width; 4448c2ecf20Sopenharmony_ci height = infmt->height; 4458c2ecf20Sopenharmony_ci if (infmt->field == V4L2_FIELD_ALTERNATE) 4468c2ecf20Sopenharmony_ci height *= 2; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Set the CSI_SENS_CONF register remaining fields */ 4498c2ecf20Sopenharmony_ci data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | 4508c2ecf20Sopenharmony_ci cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | 4518c2ecf20Sopenharmony_ci cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | 4528c2ecf20Sopenharmony_ci cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | 4538c2ecf20Sopenharmony_ci cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | 4548c2ecf20Sopenharmony_ci cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | 4558c2ecf20Sopenharmony_ci cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | 4568c2ecf20Sopenharmony_ci cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | 4578c2ecf20Sopenharmony_ci cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | 4588c2ecf20Sopenharmony_ci cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | 4598c2ecf20Sopenharmony_ci cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ipu_csi_write(csi, data, CSI_SENS_CONF); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Set CCIR registers */ 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci switch (cfg.clk_mode) { 4688c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: 4698c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); 4708c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: 4738c2ecf20Sopenharmony_ci if (width == 720 && height == 480) { 4748c2ecf20Sopenharmony_ci std = V4L2_STD_NTSC; 4758c2ecf20Sopenharmony_ci height = 525; 4768c2ecf20Sopenharmony_ci } else if (width == 720 && height == 576) { 4778c2ecf20Sopenharmony_ci std = V4L2_STD_PAL; 4788c2ecf20Sopenharmony_ci height = 625; 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci dev_err(csi->ipu->dev, 4818c2ecf20Sopenharmony_ci "Unsupported interlaced video mode\n"); 4828c2ecf20Sopenharmony_ci ret = -EINVAL; 4838c2ecf20Sopenharmony_ci goto out_unlock; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci goto out_unlock; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: 4918c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: 4928c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: 4938c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: 4948c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, 4958c2ecf20Sopenharmony_ci CSI_CCIR_CODE_1); 4968c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_GATED_CLK: 4998c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_NONGATED_CLK: 5008c2ecf20Sopenharmony_ci ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Setup sensor frame size */ 5058c2ecf20Sopenharmony_ci ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), 5068c2ecf20Sopenharmony_ci CSI_SENS_FRM_SIZE); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", 5098c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_CONF)); 5108c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", 5118c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ciout_unlock: 5148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_init_interface); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cibool ipu_csi_is_interlaced(struct ipu_csi *csi) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci unsigned long flags; 5238c2ecf20Sopenharmony_ci u32 sensor_protocol; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 5268c2ecf20Sopenharmony_ci sensor_protocol = 5278c2ecf20Sopenharmony_ci (ipu_csi_read(csi, CSI_SENS_CONF) & 5288c2ecf20Sopenharmony_ci CSI_SENS_CONF_SENS_PRTCL_MASK) >> 5298c2ecf20Sopenharmony_ci CSI_SENS_CONF_SENS_PRTCL_SHIFT; 5308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci switch (sensor_protocol) { 5338c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_GATED_CLK: 5348c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_NONGATED_CLK: 5358c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: 5368c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: 5378c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: 5388c2ecf20Sopenharmony_ci return false; 5398c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: 5408c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: 5418c2ecf20Sopenharmony_ci case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: 5428c2ecf20Sopenharmony_ci return true; 5438c2ecf20Sopenharmony_ci default: 5448c2ecf20Sopenharmony_ci dev_err(csi->ipu->dev, 5458c2ecf20Sopenharmony_ci "CSI %d sensor protocol unsupported\n", csi->id); 5468c2ecf20Sopenharmony_ci return false; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_civoid ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci unsigned long flags; 5548c2ecf20Sopenharmony_ci u32 reg; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); 5598c2ecf20Sopenharmony_ci w->width = (reg & 0xFFFF) + 1; 5608c2ecf20Sopenharmony_ci w->height = (reg >> 16 & 0xFFFF) + 1; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 5638c2ecf20Sopenharmony_ci w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; 5648c2ecf20Sopenharmony_ci w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_get_window); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_civoid ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci unsigned long flags; 5738c2ecf20Sopenharmony_ci u32 reg; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), 5788c2ecf20Sopenharmony_ci CSI_ACT_FRM_SIZE); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 5818c2ecf20Sopenharmony_ci reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); 5828c2ecf20Sopenharmony_ci reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); 5838c2ecf20Sopenharmony_ci ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_window); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_civoid ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci unsigned long flags; 5928c2ecf20Sopenharmony_ci u32 reg; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); 5978c2ecf20Sopenharmony_ci reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); 5988c2ecf20Sopenharmony_ci reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | 5998c2ecf20Sopenharmony_ci (vert ? CSI_VERT_DOWNSIZE_EN : 0); 6008c2ecf20Sopenharmony_ci ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_downsize); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_civoid ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, 6078c2ecf20Sopenharmony_ci u32 r_value, u32 g_value, u32 b_value, 6088c2ecf20Sopenharmony_ci u32 pix_clk) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci unsigned long flags; 6118c2ecf20Sopenharmony_ci u32 ipu_clk = clk_get_rate(csi->clk_ipu); 6128c2ecf20Sopenharmony_ci u32 temp; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci temp = ipu_csi_read(csi, CSI_TST_CTRL); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (!active) { 6198c2ecf20Sopenharmony_ci temp &= ~CSI_TEST_GEN_MODE_EN; 6208c2ecf20Sopenharmony_ci ipu_csi_write(csi, temp, CSI_TST_CTRL); 6218c2ecf20Sopenharmony_ci } else { 6228c2ecf20Sopenharmony_ci /* Set sensb_mclk div_ratio */ 6238c2ecf20Sopenharmony_ci ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | 6268c2ecf20Sopenharmony_ci CSI_TEST_GEN_B_MASK); 6278c2ecf20Sopenharmony_ci temp |= CSI_TEST_GEN_MODE_EN; 6288c2ecf20Sopenharmony_ci temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | 6298c2ecf20Sopenharmony_ci (g_value << CSI_TEST_GEN_G_SHIFT) | 6308c2ecf20Sopenharmony_ci (b_value << CSI_TEST_GEN_B_SHIFT); 6318c2ecf20Sopenharmony_ci ipu_csi_write(csi, temp, CSI_TST_CTRL); 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ciint ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, 6398c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct ipu_csi_bus_config cfg; 6428c2ecf20Sopenharmony_ci unsigned long flags; 6438c2ecf20Sopenharmony_ci u32 temp; 6448c2ecf20Sopenharmony_ci int ret; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (vc > 3) 6478c2ecf20Sopenharmony_ci return -EINVAL; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); 6508c2ecf20Sopenharmony_ci if (ret < 0) 6518c2ecf20Sopenharmony_ci return ret; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci temp = ipu_csi_read(csi, CSI_MIPI_DI); 6568c2ecf20Sopenharmony_ci temp &= ~(0xff << (vc * 8)); 6578c2ecf20Sopenharmony_ci temp |= (cfg.mipi_dt << (vc * 8)); 6588c2ecf20Sopenharmony_ci ipu_csi_write(csi, temp, CSI_MIPI_DI); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciint ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, 6678c2ecf20Sopenharmony_ci u32 max_ratio, u32 id) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci unsigned long flags; 6708c2ecf20Sopenharmony_ci u32 temp; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (max_ratio > 5 || id > 3) 6738c2ecf20Sopenharmony_ci return -EINVAL; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci temp = ipu_csi_read(csi, CSI_SKIP); 6788c2ecf20Sopenharmony_ci temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | 6798c2ecf20Sopenharmony_ci CSI_SKIP_SMFC_MASK); 6808c2ecf20Sopenharmony_ci temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | 6818c2ecf20Sopenharmony_ci (id << CSI_ID_2_SKIP_SHIFT) | 6828c2ecf20Sopenharmony_ci (skip << CSI_SKIP_SMFC_SHIFT); 6838c2ecf20Sopenharmony_ci ipu_csi_write(csi, temp, CSI_SKIP); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ciint ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci unsigned long flags; 6948c2ecf20Sopenharmony_ci u32 csi_sens_conf, dest; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (csi_dest == IPU_CSI_DEST_IDMAC) 6978c2ecf20Sopenharmony_ci dest = CSI_DATA_DEST_IDMAC; 6988c2ecf20Sopenharmony_ci else 6998c2ecf20Sopenharmony_ci dest = CSI_DATA_DEST_IC; /* IC or VDIC */ 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); 7048c2ecf20Sopenharmony_ci csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; 7058c2ecf20Sopenharmony_ci csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); 7068c2ecf20Sopenharmony_ci ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_set_dest); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ciint ipu_csi_enable(struct ipu_csi *csi) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci ipu_module_enable(csi->ipu, csi->module); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_enable); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ciint ipu_csi_disable(struct ipu_csi *csi) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci ipu_module_disable(csi->ipu, csi->module); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_disable); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistruct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci unsigned long flags; 7338c2ecf20Sopenharmony_ci struct ipu_csi *csi, *ret; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (id > 1) 7368c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci csi = ipu->csi_priv[id]; 7398c2ecf20Sopenharmony_ci ret = csi; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (csi->inuse) { 7448c2ecf20Sopenharmony_ci ret = ERR_PTR(-EBUSY); 7458c2ecf20Sopenharmony_ci goto unlock; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci csi->inuse = true; 7498c2ecf20Sopenharmony_ciunlock: 7508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 7518c2ecf20Sopenharmony_ci return ret; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_get); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_civoid ipu_csi_put(struct ipu_csi *csi) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci unsigned long flags; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci spin_lock_irqsave(&csi->lock, flags); 7608c2ecf20Sopenharmony_ci csi->inuse = false; 7618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&csi->lock, flags); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_put); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ciint ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, 7668c2ecf20Sopenharmony_ci unsigned long base, u32 module, struct clk *clk_ipu) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct ipu_csi *csi; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (id > 1) 7718c2ecf20Sopenharmony_ci return -ENODEV; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); 7748c2ecf20Sopenharmony_ci if (!csi) 7758c2ecf20Sopenharmony_ci return -ENOMEM; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci ipu->csi_priv[id] = csi; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci spin_lock_init(&csi->lock); 7808c2ecf20Sopenharmony_ci csi->module = module; 7818c2ecf20Sopenharmony_ci csi->id = id; 7828c2ecf20Sopenharmony_ci csi->clk_ipu = clk_ipu; 7838c2ecf20Sopenharmony_ci csi->base = devm_ioremap(dev, base, PAGE_SIZE); 7848c2ecf20Sopenharmony_ci if (!csi->base) 7858c2ecf20Sopenharmony_ci return -ENOMEM; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", 7888c2ecf20Sopenharmony_ci id, base, csi->base); 7898c2ecf20Sopenharmony_ci csi->ipu = ipu; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_civoid ipu_csi_exit(struct ipu_soc *ipu, int id) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_civoid ipu_csi_dump(struct ipu_csi *csi) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", 8018c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_CONF)); 8028c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", 8038c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); 8048c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", 8058c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); 8068c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", 8078c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); 8088c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", 8098c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_TST_CTRL)); 8108c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", 8118c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_1)); 8128c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", 8138c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_2)); 8148c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", 8158c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_CCIR_CODE_3)); 8168c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", 8178c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_MIPI_DI)); 8188c2ecf20Sopenharmony_ci dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", 8198c2ecf20Sopenharmony_ci ipu_csi_read(csi, CSI_SKIP)); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_csi_dump); 822