linux-surface/patches/5.10/0009-cameras.patch
Maximilian Luz de9f918138
Update v5.10 patches
Changes:
 - General:
   - Update patches to latest minor version, drop redundant/integrated
     patches

 - Cameras:
   - Add user controls to flip image
   - Cleanup

Links:
 - kernel: ba33f9c529
2021-01-08 00:15:52 +01:00

5906 lines
172 KiB
Diff

From 6724a7fe43dda838f3701711be5b32c4788e5a42 Mon Sep 17 00:00:00 2001
From: Sakari Ailus <sakari.ailus@linux.intel.com>
Date: Mon, 12 Oct 2020 21:04:11 +0300
Subject: [PATCH] ipu3-cio2: Use unsigned values where appropriate
Use unsigned values for width, height, bit shifts and registers,
effectively for all definitions that are not signed.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 156 +++++++++++------------
1 file changed, 78 insertions(+), 78 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 146492383aa5..7650d7998a3f 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -13,20 +13,20 @@
#define CIO2_PCI_BAR 0
#define CIO2_DMA_MASK DMA_BIT_MASK(39)
-#define CIO2_IMAGE_MAX_WIDTH 4224
-#define CIO2_IMAGE_MAX_LENGTH 3136
+#define CIO2_IMAGE_MAX_WIDTH 4224U
+#define CIO2_IMAGE_MAX_LENGTH 3136U
/* 32MB = 8xFBPT_entry */
#define CIO2_MAX_LOPS 8
#define CIO2_MAX_BUFFERS (PAGE_SIZE / 16 / CIO2_MAX_LOPS)
#define CIO2_LOP_ENTRIES (PAGE_SIZE / sizeof(u32))
-#define CIO2_PAD_SINK 0
-#define CIO2_PAD_SOURCE 1
-#define CIO2_PADS 2
+#define CIO2_PAD_SINK 0U
+#define CIO2_PAD_SOURCE 1U
+#define CIO2_PADS 2U
-#define CIO2_NUM_DMA_CHAN 20
-#define CIO2_NUM_PORTS 4 /* DPHYs */
+#define CIO2_NUM_DMA_CHAN 20U
+#define CIO2_NUM_PORTS 4U /* DPHYs */
/* 1 for each sensor */
#define CIO2_QUEUES CIO2_NUM_PORTS
@@ -66,12 +66,12 @@
#define CIO2_REG_MIPIBE_FORCE_RAW8 (CIO2_REG_MIPIBE_BASE + 0x20)
#define CIO2_REG_MIPIBE_FORCE_RAW8_ENABLE BIT(0)
#define CIO2_REG_MIPIBE_FORCE_RAW8_USE_TYPEID BIT(1)
-#define CIO2_REG_MIPIBE_FORCE_RAW8_TYPEID_SHIFT 2
+#define CIO2_REG_MIPIBE_FORCE_RAW8_TYPEID_SHIFT 2U
#define CIO2_REG_MIPIBE_IRQ_STATUS (CIO2_REG_MIPIBE_BASE + 0x24)
#define CIO2_REG_MIPIBE_IRQ_CLEAR (CIO2_REG_MIPIBE_BASE + 0x28)
#define CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD (CIO2_REG_MIPIBE_BASE + 0x68)
-#define CIO2_MIPIBE_GLOBAL_LUT_DISREGARD 1
+#define CIO2_MIPIBE_GLOBAL_LUT_DISREGARD 1U
#define CIO2_REG_MIPIBE_PKT_STALL_STATUS (CIO2_REG_MIPIBE_BASE + 0x6c)
#define CIO2_REG_MIPIBE_PARSE_GSP_THROUGH_LP_LUT_REG_IDX \
(CIO2_REG_MIPIBE_BASE + 0x70)
@@ -79,10 +79,10 @@
(CIO2_REG_MIPIBE_BASE + 0x74 + 4 * (vc))
#define CIO2_REG_MIPIBE_LP_LUT_ENTRY(m) /* m = 0..15 */ \
(CIO2_REG_MIPIBE_BASE + 0x84 + 4 * (m))
-#define CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD 1
-#define CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT 1
-#define CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT 5
-#define CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT 7
+#define CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD 1U
+#define CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT 1U
+#define CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT 5U
+#define CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT 7U
/* base register: CIO2_REG_PIPE_BASE(pipe) * CIO2_REG_IRQCTRL_BASE */
/* IRQ registers are 18-bit wide, see cio2_irq_error for bit definitions */
@@ -113,31 +113,31 @@
#define CIO2_CGC_ROSC_DCGE BIT(12)
#define CIO2_CGC_XOSC_DCGE BIT(13)
#define CIO2_CGC_FLIS_DCGE BIT(14)
-#define CIO2_CGC_CLKGATE_HOLDOFF_SHIFT 20
-#define CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT 24
+#define CIO2_CGC_CLKGATE_HOLDOFF_SHIFT 20U
+#define CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT 24U
#define CIO2_REG_D0I3C 0x1408
#define CIO2_D0I3C_I3 BIT(2) /* Set D0I3 */
#define CIO2_D0I3C_RR BIT(3) /* Restore? */
#define CIO2_REG_SWRESET 0x140c
-#define CIO2_SWRESET_SWRESET 1
+#define CIO2_SWRESET_SWRESET 1U
#define CIO2_REG_SENSOR_ACTIVE 0x1410
#define CIO2_REG_INT_STS 0x1414
#define CIO2_REG_INT_STS_EXT_OE 0x1418
-#define CIO2_INT_EXT_OE_DMAOE_SHIFT 0
+#define CIO2_INT_EXT_OE_DMAOE_SHIFT 0U
#define CIO2_INT_EXT_OE_DMAOE_MASK 0x7ffff
-#define CIO2_INT_EXT_OE_OES_SHIFT 24
+#define CIO2_INT_EXT_OE_OES_SHIFT 24U
#define CIO2_INT_EXT_OE_OES_MASK (0xf << CIO2_INT_EXT_OE_OES_SHIFT)
#define CIO2_REG_INT_EN 0x1420
#define CIO2_REG_INT_EN_IRQ (1 << 24)
-#define CIO2_REG_INT_EN_IOS(dma) (1 << (((dma) >> 1) + 12))
+#define CIO2_REG_INT_EN_IOS(dma) (1U << (((dma) >> 1U) + 12U))
/*
* Interrupt on completion bit, Eg. DMA 0-3 maps to bit 0-3,
* DMA4 & DMA5 map to bit 4 ... DMA18 & DMA19 map to bit 11 Et cetera
*/
-#define CIO2_INT_IOC(dma) (1 << ((dma) < 4 ? (dma) : ((dma) >> 1) + 2))
+#define CIO2_INT_IOC(dma) (1U << ((dma) < 4U ? (dma) : ((dma) >> 1U) + 2U))
#define CIO2_INT_IOC_SHIFT 0
#define CIO2_INT_IOC_MASK (0x7ff << CIO2_INT_IOC_SHIFT)
-#define CIO2_INT_IOS_IOLN(dma) (1 << (((dma) >> 1) + 12))
+#define CIO2_INT_IOS_IOLN(dma) (1U << (((dma) >> 1U) + 12U))
#define CIO2_INT_IOS_IOLN_SHIFT 12
#define CIO2_INT_IOS_IOLN_MASK (0x3ff << CIO2_INT_IOS_IOLN_SHIFT)
#define CIO2_INT_IOIE BIT(22)
@@ -145,32 +145,32 @@
#define CIO2_INT_IOIRQ BIT(24)
#define CIO2_REG_INT_EN_EXT_OE 0x1424
#define CIO2_REG_DMA_DBG 0x1448
-#define CIO2_REG_DMA_DBG_DMA_INDEX_SHIFT 0
+#define CIO2_REG_DMA_DBG_DMA_INDEX_SHIFT 0U
#define CIO2_REG_PBM_ARB_CTRL 0x1460
-#define CIO2_PBM_ARB_CTRL_LANES_DIV 0 /* 4-4-2-2 lanes */
-#define CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT 0
+#define CIO2_PBM_ARB_CTRL_LANES_DIV 0U /* 4-4-2-2 lanes */
+#define CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT 0U
#define CIO2_PBM_ARB_CTRL_LE_EN BIT(7)
-#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN 2
-#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT 8
-#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP 480
-#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT 16
+#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN 2U
+#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT 8U
+#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP 480U
+#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT 16U
#define CIO2_REG_PBM_WMCTRL1 0x1464
-#define CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT 0
-#define CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT 8
-#define CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT 16
+#define CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT 0U
+#define CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT 8U
+#define CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT 16U
#define CIO2_PBM_WMCTRL1_TS_COUNT_DISABLE BIT(31)
#define CIO2_PBM_WMCTRL1_MIN_2CK (4 << CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT)
#define CIO2_PBM_WMCTRL1_MID1_2CK (16 << CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT)
#define CIO2_PBM_WMCTRL1_MID2_2CK (21 << CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT)
#define CIO2_REG_PBM_WMCTRL2 0x1468
-#define CIO2_PBM_WMCTRL2_HWM_2CK 40
-#define CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT 0
-#define CIO2_PBM_WMCTRL2_LWM_2CK 22
-#define CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT 8
-#define CIO2_PBM_WMCTRL2_OBFFWM_2CK 2
-#define CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT 16
-#define CIO2_PBM_WMCTRL2_TRANSDYN 1
-#define CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT 24
+#define CIO2_PBM_WMCTRL2_HWM_2CK 40U
+#define CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT 0U
+#define CIO2_PBM_WMCTRL2_LWM_2CK 22U
+#define CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT 8U
+#define CIO2_PBM_WMCTRL2_OBFFWM_2CK 2U
+#define CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT 16U
+#define CIO2_PBM_WMCTRL2_TRANSDYN 1U
+#define CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT 24U
#define CIO2_PBM_WMCTRL2_DYNWMEN BIT(28)
#define CIO2_PBM_WMCTRL2_OBFF_MEM_EN BIT(29)
#define CIO2_PBM_WMCTRL2_OBFF_CPU_EN BIT(30)
@@ -178,12 +178,12 @@
#define CIO2_REG_PBM_TS_COUNT 0x146c
#define CIO2_REG_PBM_FOPN_ABORT 0x1474
/* below n = 0..3 */
-#define CIO2_PBM_FOPN_ABORT(n) (0x1 << 8 * (n))
-#define CIO2_PBM_FOPN_FORCE_ABORT(n) (0x2 << 8 * (n))
-#define CIO2_PBM_FOPN_FRAMEOPEN(n) (0x8 << 8 * (n))
+#define CIO2_PBM_FOPN_ABORT(n) (0x1 << 8U * (n))
+#define CIO2_PBM_FOPN_FORCE_ABORT(n) (0x2 << 8U * (n))
+#define CIO2_PBM_FOPN_FRAMEOPEN(n) (0x8 << 8U * (n))
#define CIO2_REG_LTRCTRL 0x1480
#define CIO2_LTRCTRL_LTRDYNEN BIT(16)
-#define CIO2_LTRCTRL_LTRSTABLETIME_SHIFT 8
+#define CIO2_LTRCTRL_LTRSTABLETIME_SHIFT 8U
#define CIO2_LTRCTRL_LTRSTABLETIME_MASK 0xff
#define CIO2_LTRCTRL_LTRSEL1S3 BIT(7)
#define CIO2_LTRCTRL_LTRSEL1S2 BIT(6)
@@ -195,28 +195,28 @@
#define CIO2_LTRCTRL_LTRSEL2S0 BIT(0)
#define CIO2_REG_LTRVAL23 0x1484
#define CIO2_REG_LTRVAL01 0x1488
-#define CIO2_LTRVAL02_VAL_SHIFT 0
-#define CIO2_LTRVAL02_SCALE_SHIFT 10
-#define CIO2_LTRVAL13_VAL_SHIFT 16
-#define CIO2_LTRVAL13_SCALE_SHIFT 26
+#define CIO2_LTRVAL02_VAL_SHIFT 0U
+#define CIO2_LTRVAL02_SCALE_SHIFT 10U
+#define CIO2_LTRVAL13_VAL_SHIFT 16U
+#define CIO2_LTRVAL13_SCALE_SHIFT 26U
-#define CIO2_LTRVAL0_VAL 175
+#define CIO2_LTRVAL0_VAL 175U
/* Value times 1024 ns */
-#define CIO2_LTRVAL0_SCALE 2
-#define CIO2_LTRVAL1_VAL 90
-#define CIO2_LTRVAL1_SCALE 2
-#define CIO2_LTRVAL2_VAL 90
-#define CIO2_LTRVAL2_SCALE 2
-#define CIO2_LTRVAL3_VAL 90
-#define CIO2_LTRVAL3_SCALE 2
+#define CIO2_LTRVAL0_SCALE 2U
+#define CIO2_LTRVAL1_VAL 90U
+#define CIO2_LTRVAL1_SCALE 2U
+#define CIO2_LTRVAL2_VAL 90U
+#define CIO2_LTRVAL2_SCALE 2U
+#define CIO2_LTRVAL3_VAL 90U
+#define CIO2_LTRVAL3_SCALE 2U
#define CIO2_REG_CDMABA(n) (0x1500 + 0x10 * (n)) /* n = 0..19 */
#define CIO2_REG_CDMARI(n) (0x1504 + 0x10 * (n))
-#define CIO2_CDMARI_FBPT_RP_SHIFT 0
+#define CIO2_CDMARI_FBPT_RP_SHIFT 0U
#define CIO2_CDMARI_FBPT_RP_MASK 0xff
#define CIO2_REG_CDMAC0(n) (0x1508 + 0x10 * (n))
-#define CIO2_CDMAC0_FBPT_LEN_SHIFT 0
-#define CIO2_CDMAC0_FBPT_WIDTH_SHIFT 8
+#define CIO2_CDMAC0_FBPT_LEN_SHIFT 0U
+#define CIO2_CDMAC0_FBPT_WIDTH_SHIFT 8U
#define CIO2_CDMAC0_FBPT_NS BIT(25)
#define CIO2_CDMAC0_DMA_INTR_ON_FS BIT(26)
#define CIO2_CDMAC0_DMA_INTR_ON_FE BIT(27)
@@ -225,12 +225,12 @@
#define CIO2_CDMAC0_DMA_EN BIT(30)
#define CIO2_CDMAC0_DMA_HALTED BIT(31)
#define CIO2_REG_CDMAC1(n) (0x150c + 0x10 * (n))
-#define CIO2_CDMAC1_LINENUMINT_SHIFT 0
-#define CIO2_CDMAC1_LINENUMUPDATE_SHIFT 16
+#define CIO2_CDMAC1_LINENUMINT_SHIFT 0U
+#define CIO2_CDMAC1_LINENUMUPDATE_SHIFT 16U
/* n = 0..3 */
#define CIO2_REG_PXM_PXF_FMT_CFG0(n) (0x1700 + 0x30 * (n))
-#define CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT 0
-#define CIO2_PXM_PXF_FMT_CFG_SID1_SHIFT 16
+#define CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT 0U
+#define CIO2_PXM_PXF_FMT_CFG_SID1_SHIFT 16U
#define CIO2_PXM_PXF_FMT_CFG_PCK_64B (0 << 0)
#define CIO2_PXM_PXF_FMT_CFG_PCK_32B (1 << 0)
#define CIO2_PXM_PXF_FMT_CFG_BPP_08 (0 << 2)
@@ -249,27 +249,27 @@
#define CIO2_PXM_PXF_FMT_CFG_PSWAP4_2ND_BD (1 << 10)
#define CIO2_REG_INT_STS_EXT_IE 0x17e4
#define CIO2_REG_INT_EN_EXT_IE 0x17e8
-#define CIO2_INT_EXT_IE_ECC_RE(n) (0x01 << (8 * (n)))
-#define CIO2_INT_EXT_IE_DPHY_NR(n) (0x02 << (8 * (n)))
-#define CIO2_INT_EXT_IE_ECC_NR(n) (0x04 << (8 * (n)))
-#define CIO2_INT_EXT_IE_CRCERR(n) (0x08 << (8 * (n)))
-#define CIO2_INT_EXT_IE_INTERFRAMEDATA(n) (0x10 << (8 * (n)))
-#define CIO2_INT_EXT_IE_PKT2SHORT(n) (0x20 << (8 * (n)))
-#define CIO2_INT_EXT_IE_PKT2LONG(n) (0x40 << (8 * (n)))
-#define CIO2_INT_EXT_IE_IRQ(n) (0x80 << (8 * (n)))
+#define CIO2_INT_EXT_IE_ECC_RE(n) (0x01 << (8U * (n)))
+#define CIO2_INT_EXT_IE_DPHY_NR(n) (0x02 << (8U * (n)))
+#define CIO2_INT_EXT_IE_ECC_NR(n) (0x04 << (8U * (n)))
+#define CIO2_INT_EXT_IE_CRCERR(n) (0x08 << (8U * (n)))
+#define CIO2_INT_EXT_IE_INTERFRAMEDATA(n) (0x10 << (8U * (n)))
+#define CIO2_INT_EXT_IE_PKT2SHORT(n) (0x20 << (8U * (n)))
+#define CIO2_INT_EXT_IE_PKT2LONG(n) (0x40 << (8U * (n)))
+#define CIO2_INT_EXT_IE_IRQ(n) (0x80 << (8U * (n)))
#define CIO2_REG_PXM_FRF_CFG(n) (0x1720 + 0x30 * (n))
#define CIO2_PXM_FRF_CFG_FNSEL BIT(0)
#define CIO2_PXM_FRF_CFG_FN_RST BIT(1)
#define CIO2_PXM_FRF_CFG_ABORT BIT(2)
-#define CIO2_PXM_FRF_CFG_CRC_TH_SHIFT 3
+#define CIO2_PXM_FRF_CFG_CRC_TH_SHIFT 3U
#define CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR BIT(8)
#define CIO2_PXM_FRF_CFG_MSK_ECC_RE BIT(9)
#define CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE BIT(10)
-#define CIO2_PXM_FRF_CFG_EVEN_ODD_MODE_SHIFT 11
+#define CIO2_PXM_FRF_CFG_EVEN_ODD_MODE_SHIFT 11U
#define CIO2_PXM_FRF_CFG_MASK_CRC_THRES BIT(13)
#define CIO2_PXM_FRF_CFG_MASK_CSI_ACCEPT BIT(14)
#define CIO2_PXM_FRF_CFG_CIOHC_FS_MODE BIT(15)
-#define CIO2_PXM_FRF_CFG_CIOHC_FRST_FRM_SHIFT 16
+#define CIO2_PXM_FRF_CFG_CIOHC_FRST_FRM_SHIFT 16U
#define CIO2_REG_PXM_SID2BID0(n) (0x1724 + 0x30 * (n))
#define CIO2_FB_HPLL_FREQ 0x2
#define CIO2_ISCLK_RATIO 0xc
@@ -278,14 +278,14 @@
#define CIO2_INT_EN_EXT_OE_MASK 0x8f0fffff
-#define CIO2_CGC_CLKGATE_HOLDOFF 3
-#define CIO2_CGC_CSI_CLKGATE_HOLDOFF 5
+#define CIO2_CGC_CLKGATE_HOLDOFF 3U
+#define CIO2_CGC_CSI_CLKGATE_HOLDOFF 5U
#define CIO2_PXM_FRF_CFG_CRC_TH 16
#define CIO2_INT_EN_EXT_IE_MASK 0xffffffff
-#define CIO2_DMA_CHAN 0
+#define CIO2_DMA_CHAN 0U
#define CIO2_CSIRX_DLY_CNT_CLANE_IDX -1
@@ -302,8 +302,8 @@
#define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4
#define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570
-#define CIO2_PMCSR_OFFSET 4
-#define CIO2_PMCSR_D0D3_SHIFT 2
+#define CIO2_PMCSR_OFFSET 4U
+#define CIO2_PMCSR_D0D3_SHIFT 2U
#define CIO2_PMCSR_D3 0x3
struct cio2_csi2_timing {
--
2.30.0
From a4a1839d13e72b1ca7c7f052b06e435d7ac0835c Mon Sep 17 00:00:00 2001
From: Sakari Ailus <sakari.ailus@linux.intel.com>
Date: Mon, 12 Oct 2020 21:04:12 +0300
Subject: [PATCH] ipu3-cio2: Remove explicit type from frame size checks
Now that the values are unsigned, we can remove explicit cast to u32.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 1fcd131482e0..b2679ff185fc 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1283,9 +1283,8 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
}
}
- fmt->format.width = min_t(u32, fmt->format.width, CIO2_IMAGE_MAX_WIDTH);
- fmt->format.height = min_t(u32, fmt->format.height,
- CIO2_IMAGE_MAX_LENGTH);
+ fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH);
+ fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_LENGTH);
fmt->format.field = V4L2_FIELD_NONE;
mutex_lock(&q->subdev_lock);
--
2.30.0
From ee88966296444b44a6e02c6048f0225513175682 Mon Sep 17 00:00:00 2001
From: Sakari Ailus <sakari.ailus@linux.intel.com>
Date: Mon, 12 Oct 2020 21:04:13 +0300
Subject: [PATCH] ipu3-cio2: Rename CIO2_IMAGE_MAX_LENGTH as
CIO2_IMAGE_MAX_HEIGHT
CIO2_IMAGE_MAX_LENGTH is the maximum width of the image. Rename it as
"CIO2_IMAGE_MAX_HEIGHT" in order to better describe what it is.
Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2.c | 6 +++---
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index b2679ff185fc..51c4dd6a8f9a 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1095,8 +1095,8 @@ static int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
/* Only supports up to 4224x3136 */
if (mpix->width > CIO2_IMAGE_MAX_WIDTH)
mpix->width = CIO2_IMAGE_MAX_WIDTH;
- if (mpix->height > CIO2_IMAGE_MAX_LENGTH)
- mpix->height = CIO2_IMAGE_MAX_LENGTH;
+ if (mpix->height > CIO2_IMAGE_MAX_HEIGHT)
+ mpix->height = CIO2_IMAGE_MAX_HEIGHT;
mpix->num_planes = 1;
mpix->pixelformat = fmt->fourcc;
@@ -1284,7 +1284,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
}
fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH);
- fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_LENGTH);
+ fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT);
fmt->format.field = V4L2_FIELD_NONE;
mutex_lock(&q->subdev_lock);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 7650d7998a3f..ccf0b85ae36f 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -14,7 +14,7 @@
#define CIO2_DMA_MASK DMA_BIT_MASK(39)
#define CIO2_IMAGE_MAX_WIDTH 4224U
-#define CIO2_IMAGE_MAX_LENGTH 3136U
+#define CIO2_IMAGE_MAX_HEIGHT 3136U
/* 32MB = 8xFBPT_entry */
#define CIO2_MAX_LOPS 8
--
2.30.0
From 3eff25474b3f65e7acfa1c2fc5b4fffae3114f96 Mon Sep 17 00:00:00 2001
From: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
Date: Tue, 13 Oct 2020 17:25:35 +0300
Subject: [PATCH] ipu3-cio2: Check receved the size against payload size, not
buffer size
Compare the received size of the payload size, not the allocated size of
the buffer that is page aligned. This way also images that aren't aligned
to page size are not warned about.
Also wrap a line over 80 characters.
Suggested-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
Reviewed-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
Tested-by: Jean-Michel Hautbois <jeanmichel.hautbois@xxxxxxxxx>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 51c4dd6a8f9a..c557d189200b 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -561,7 +561,9 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan)
b = q->bufs[q->bufs_first];
if (b) {
- unsigned int bytes = entry[1].second_entry.num_of_bytes;
+ unsigned int received = entry[1].second_entry.num_of_bytes;
+ unsigned long payload =
+ vb2_get_plane_payload(&b->vbb.vb2_buf, 0);
q->bufs[q->bufs_first] = NULL;
atomic_dec(&q->bufs_queued);
@@ -571,10 +573,10 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan)
b->vbb.vb2_buf.timestamp = ns;
b->vbb.field = V4L2_FIELD_NONE;
b->vbb.sequence = atomic_read(&q->frame_sequence);
- if (b->vbb.vb2_buf.planes[0].length != bytes)
- dev_warn(dev, "buffer length is %d received %d\n",
- b->vbb.vb2_buf.planes[0].length,
- bytes);
+ if (payload != received)
+ dev_warn(dev,
+ "payload length is %lu, received %u\n",
+ payload, received);
vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE);
}
atomic_inc(&q->frame_sequence);
--
2.30.0
From 2d2ff25aedd907713bbfa66ce02de3edb0d99093 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 24 Oct 2020 22:42:28 +0100
Subject: [PATCH] property: Return true in fwnode_device_is_available for node
types that do not implement this operation
Some types of fwnode_handle do not implement the device_is_available()
check, such as those created by software_nodes. There isn't really a
meaningful way to check for the availability of a device that doesn't
actually exist, so if the check isn't implemented just assume that the
"device" is present.
Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/property.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 4c43d30145c6..a5ca2306796f 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -785,9 +785,14 @@ EXPORT_SYMBOL_GPL(fwnode_handle_put);
/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
+ *
+ * For fwnode node types that don't implement the .device_is_available()
+ * operation, this function returns true.
*/
bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
{
+ if (!fwnode_has_op(fwnode, device_is_available))
+ return true;
return fwnode_call_bool_op(fwnode, device_is_available);
}
EXPORT_SYMBOL_GPL(fwnode_device_is_available);
--
2.30.0
From fbe20186e97d7f425d2280d5d98e0804c91c3710 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 21 Nov 2020 22:06:38 +0000
Subject: [PATCH] property: Add support for calling
fwnode_graph_get_endpoint_by_id() for fwnode->secondary
This function is used to find fwnode endpoints against a device. In
some instances those endpoints are software nodes which are children of
fwnode->secondary. Add support to fwnode_graph_get_endpoint_by_id() to
find those endpoints by recursively calling itself passing the ptr to
fwnode->secondary in the event no endpoint is found for the primary.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/property.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index a5ca2306796f..4ece6b086e36 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -1162,6 +1162,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode,
best_ep_id = fwnode_ep.id;
}
+ if (!best_ep && fwnode && !IS_ERR_OR_NULL(fwnode->secondary))
+ return fwnode_graph_get_endpoint_by_id(fwnode->secondary, port,
+ endpoint, flags);
+
return best_ep;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id);
--
2.30.0
From 8c4ccad522391bef6e526313ebe6a48c734e2b7b Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 23:42:09 +0100
Subject: [PATCH] software_node: Fix failure to put() and get() references to
children in software_node_get_next_child()
The software_node_get_next_child() function currently does not hold
references to the child software_node that it finds or put the ref that
is held against the old child - fix that.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 010828fc785b..615a0c93e116 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -443,14 +443,18 @@ software_node_get_next_child(const struct fwnode_handle *fwnode,
struct swnode *c = to_swnode(child);
if (!p || list_empty(&p->children) ||
- (c && list_is_last(&c->entry, &p->children)))
+ (c && list_is_last(&c->entry, &p->children))) {
+ fwnode_handle_put(child);
return NULL;
+ }
if (c)
c = list_next_entry(c, entry);
else
c = list_first_entry(&p->children, struct swnode, entry);
- return &c->fwnode;
+
+ fwnode_handle_put(child);
+ return fwnode_handle_get(&c->fwnode);
}
static struct fwnode_handle *
--
2.30.0
From 83d8bf9a179ffdfbea4d49190723c2121e2f6cf7 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 25 Oct 2020 22:49:08 +0000
Subject: [PATCH] software_node: Enforce parent before child ordering of nodes
array for software_node_register_nodes()
Registering software_nodes with the .parent member set to point to a
currently unregistered software_node has the potential for problems,
so enforce parent -> child ordering in arrays passed to this function.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 615a0c93e116..af7930b3679e 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -700,14 +700,21 @@ int software_node_register_nodes(const struct software_node *nodes)
int i;
for (i = 0; nodes[i].name; i++) {
+ if (nodes[i].parent)
+ if (!software_node_to_swnode(nodes[i].parent)) {
+ ret = -EINVAL;
+ goto err_unregister_nodes;
+ }
+
ret = software_node_register(&nodes[i]);
- if (ret) {
- software_node_unregister_nodes(nodes);
- return ret;
- }
+ if (ret)
+ goto err_unregister_nodes;
}
return 0;
+err_unregister_nodes:
+ software_node_unregister_nodes(nodes);
+ return ret;
}
EXPORT_SYMBOL_GPL(software_node_register_nodes);
--
2.30.0
From a79416727dfa1e3be06459a9f9a53f09cbde73ee Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 22:16:38 +0100
Subject: [PATCH] software_node: Alter software_node_unregister_nodes() to
unregister the array in reverse order
Software nodes that are children of another software node should be
unregistered before their parent. To allow easy unregistering of an array
of software_nodes ordered parent to child, reverse the order in which
this function unregisters software_nodes.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index af7930b3679e..d39e1c76d98d 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -720,20 +720,25 @@ EXPORT_SYMBOL_GPL(software_node_register_nodes);
/**
* software_node_unregister_nodes - Unregister an array of software nodes
- * @nodes: Zero terminated array of software nodes to be unregistered
+ * @nodes: Zero terminated array of software nodes to be unregistered. If
+ * parent pointers are set up in any of the software nodes then the array
+ * MUST be ordered such that parents come before their children.
*
* Unregister multiple software nodes at once.
*
- * NOTE: Be careful using this call if the nodes had parent pointers set up in
- * them before registering. If so, it is wiser to remove the nodes
- * individually, in the correct order (child before parent) instead of relying
- * on the sequential order of the list of nodes in the array.
+ * NOTE: If you are uncertain whether the array is ordered such that
+ * parents will be unregistered before their children, it is wiser to
+ * remove the nodes individually, in the correct order (child before
+ * parent).
*/
void software_node_unregister_nodes(const struct software_node *nodes)
{
- int i;
+ unsigned int i = 0;
+
+ while (nodes[i].name)
+ i++;
- for (i = 0; nodes[i].name; i++)
+ while (i--)
software_node_unregister(&nodes[i]);
}
EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
--
2.30.0
From e2173d849ceec4f6f54386d8c95d5b5bc89c1293 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 22:25:03 +0100
Subject: [PATCH] software_node: amend software_node_unregister_node_group() to
perform unregistration of array in reverse order to be consistent with
software_node_unregister_nodes()
To maintain consistency with software_node_unregister_nodes(), reverse
the order in which the software_node_unregister_node_group() function
unregisters nodes.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index d39e1c76d98d..9bd0bb77ad5b 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -782,7 +782,10 @@ void software_node_unregister_node_group(const struct software_node **node_group
if (!node_group)
return;
- for (i = 0; node_group[i]; i++)
+ while (node_group[i]->name)
+ i++;
+
+ while (i--)
software_node_unregister(node_group[i]);
}
EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
--
2.30.0
From 32609e5229c8bb29efde6d1e55ae704a1d932827 Mon Sep 17 00:00:00 2001
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Date: Tue, 15 Sep 2020 15:47:46 +0100
Subject: [PATCH] software_node: Add support for fwnode_graph*() family of
functions
This implements the remaining .graph_* callbacks in the
fwnode operations vector for the software nodes. That makes
the fwnode_graph*() functions available in the drivers also
when software nodes are used.
The implementation tries to mimic the "OF graph" as much as
possible, but there is no support for the "reg" device
property. The ports will need to have the index in their
name which starts with "port" (for example "port0", "port1",
...) and endpoints will use the index of the software node
that is given to them during creation. The port nodes can
also be grouped under a specially named "ports" subnode,
just like in DT, if necessary.
The remote-endpoints are reference properties under the
endpoint nodes that are named "remote-endpoint".
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Co-developed-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 110 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 109 insertions(+), 1 deletion(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 9bd0bb77ad5b..0c7a8d6b9ea8 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -540,6 +540,110 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
return 0;
}
+static struct fwnode_handle *
+swnode_graph_find_next_port(const struct fwnode_handle *parent,
+ struct fwnode_handle *port)
+{
+ struct fwnode_handle *old = port;
+
+ while ((port = software_node_get_next_child(parent, old))) {
+ if (!strncmp(to_swnode(port)->node->name, "port", 4))
+ return port;
+ old = port;
+ }
+
+ return NULL;
+}
+
+static struct fwnode_handle *
+software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *endpoint)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ struct fwnode_handle *old = endpoint;
+ struct fwnode_handle *parent;
+ struct fwnode_handle *port;
+
+ if (!swnode)
+ return NULL;
+
+ if (endpoint) {
+ port = software_node_get_parent(endpoint);
+ parent = software_node_get_parent(port);
+ } else {
+ parent = software_node_get_named_child_node(fwnode, "ports");
+ if (!parent)
+ parent = software_node_get(&swnode->fwnode);
+
+ port = swnode_graph_find_next_port(parent, NULL);
+ }
+
+ for (; port; port = swnode_graph_find_next_port(parent, port)) {
+ endpoint = software_node_get_next_child(port, old);
+ if (endpoint) {
+ fwnode_handle_put(port);
+ break;
+ }
+
+ /* No more endpoints for that port, so stop passing old */
+ old = NULL;
+ }
+
+ fwnode_handle_put(parent);
+
+ return endpoint;
+}
+
+static struct fwnode_handle *
+software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ const struct software_node_ref_args *ref;
+ const struct property_entry *prop;
+
+ if (!swnode)
+ return NULL;
+
+ prop = property_entry_get(swnode->node->properties, "remote-endpoint");
+ if (!prop || prop->type != DEV_PROP_REF || prop->is_inline)
+ return NULL;
+
+ ref = prop->pointer;
+
+ return software_node_get(software_node_fwnode(ref[0].node));
+}
+
+static struct fwnode_handle *
+software_node_graph_get_port_parent(struct fwnode_handle *fwnode)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ struct fwnode_handle *parent;
+
+ if (!strcmp(swnode->parent->node->name, "ports"))
+ parent = &swnode->parent->parent->fwnode;
+ else
+ parent = &swnode->parent->fwnode;
+
+ return software_node_get(parent);
+}
+
+static int
+software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
+ struct fwnode_endpoint *endpoint)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ int ret;
+
+ ret = kstrtou32(swnode->parent->node->name + 4, 10, &endpoint->port);
+ if (ret)
+ return ret;
+
+ endpoint->id = swnode->id;
+ endpoint->local_fwnode = fwnode;
+
+ return 0;
+}
+
static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
@@ -551,7 +655,11 @@ static const struct fwnode_operations software_node_ops = {
.get_parent = software_node_get_parent,
.get_next_child_node = software_node_get_next_child,
.get_named_child_node = software_node_get_named_child_node,
- .get_reference_args = software_node_get_reference_args
+ .get_reference_args = software_node_get_reference_args,
+ .graph_get_next_endpoint = software_node_graph_get_next_endpoint,
+ .graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
+ .graph_get_port_parent = software_node_graph_get_port_parent,
+ .graph_parse_endpoint = software_node_graph_parse_endpoint,
};
/* -------------------------------------------------------------------------- */
--
2.30.0
From 978da96368c650761c10c831f75c8a6d70f7d280 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 23:07:22 +0100
Subject: [PATCH] lib/test_printf.c: Use helper function to unwind array of
software_nodes
Use the software_node_unregister_nodes() helper function to unwind this
array in a cleaner way.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
lib/test_printf.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 7ac87f18a10f..7d60f24240a4 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -644,9 +644,7 @@ static void __init fwnode_pointer(void)
test(second_name, "%pfwP", software_node_fwnode(&softnodes[1]));
test(third_name, "%pfwP", software_node_fwnode(&softnodes[2]));
- software_node_unregister(&softnodes[2]);
- software_node_unregister(&softnodes[1]);
- software_node_unregister(&softnodes[0]);
+ software_node_unregister_nodes(softnodes);
}
static void __init
--
2.30.0
From 325fd20fa2707b688748b9cc4e3e984c56dd0700 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 23:11:36 +0100
Subject: [PATCH] ipu3-cio2: Add T: entry to MAINTAINERS
Development for the ipu3-cio2 driver is taking place in media_tree, but
there's no T: entry in MAINTAINERS to denote that - rectify that oversight
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 281de213ef47..5a1c6e959aa8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8938,6 +8938,7 @@ M: Bingbu Cao <bingbu.cao@intel.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
+T: git git://linuxtv.org/media_tree.git
F: Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst
F: drivers/media/pci/intel/ipu3/
--
2.30.0
From 38dcf34032f38521a24e767d6b9224d15fd456b8 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 22:47:21 +0100
Subject: [PATCH] ipu3-cio2: Rename ipu3-cio2.c to allow module to be built
from multiple source files retaining ipu3-cio2 name
ipu3-cio2 driver needs extending with multiple files; rename the main
source file and specify the renamed file in Makefile to accommodate that.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/Makefile | 2 ++
drivers/media/pci/intel/ipu3/{ipu3-cio2.c => ipu3-cio2-main.c} | 0
2 files changed, 2 insertions(+)
rename drivers/media/pci/intel/ipu3/{ipu3-cio2.c => ipu3-cio2-main.c} (100%)
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index 98ddd5beafe0..429d516452e4 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
+
+ipu3-cio2-y += ipu3-cio2-main.o
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
similarity index 100%
rename from drivers/media/pci/intel/ipu3/ipu3-cio2.c
rename to drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
--
2.30.0
From 74556421e7287ffce9ff0155df832094dd352ed1 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 21:53:05 +0100
Subject: [PATCH] media: v4l2-core: v4l2-async: Check possible match in
match_fwnode based on sd->fwnode->secondary
Where the fwnode graph is comprised of software_nodes, these will be
assigned as the secondary to dev->fwnode. Check the v4l2_subdev's fwnode
for a secondary and attempt to match against it during match_fwnode() to
accommodate that possibility.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/v4l2-core/v4l2-async.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index e3ab003a6c85..6486dbde784f 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -87,6 +87,14 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier,
if (sd->fwnode == asd->match.fwnode)
return true;
+ /*
+ * Check the same situation for any possible secondary assigned to the
+ * subdev's fwnode
+ */
+ if ((!IS_ERR_OR_NULL(sd->fwnode->secondary)) &&
+ sd->fwnode->secondary == asd->match.fwnode)
+ return true;
+
/*
* Otherwise, check if the sd fwnode and the asd fwnode refer to an
* endpoint or a device. If they're of the same type, there's no match.
--
2.30.0
From 902528ae0440be5e64107effc15efe0b8b4fcc71 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 15 Nov 2020 08:15:34 +0000
Subject: [PATCH] acpi: Add acpi_dev_get_next_match_dev() and macro to iterate
through acpi_devices matching a given _HID
To ensure we handle situations in which multiple sensors of the same
model (and therefore _HID) are present in a system, we need to be able
to iterate over devices matching a known _HID but unknown _UID and _HRV
- add acpi_dev_get_next_match_dev() to accommodate that possibility and
change acpi_dev_get_first_match_dev() to simply call the new function
with a NULL starting point. Add an iterator macro for convenience.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/utils.c | 30 ++++++++++++++++++++++++++----
include/acpi/acpi_bus.h | 7 +++++++
2 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index d5411a166685..c177165c8db2 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -843,12 +843,13 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
EXPORT_SYMBOL(acpi_dev_present);
/**
- * acpi_dev_get_first_match_dev - Return the first match of ACPI device
+ * acpi_dev_get_next_match_dev - Return the next match of ACPI device
+ * @adev: Pointer to the previous acpi_device matching this hid, uid and hrv
* @hid: Hardware ID of the device.
* @uid: Unique ID of the device, pass NULL to not check _UID
* @hrv: Hardware Revision of the device, pass -1 to not check _HRV
*
- * Return the first match of ACPI device if a matching device was present
+ * Return the next match of ACPI device if another matching device was present
* at the moment of invocation, or NULL otherwise.
*
* The caller is responsible to call put_device() on the returned device.
@@ -856,8 +857,9 @@ EXPORT_SYMBOL(acpi_dev_present);
* See additional information in acpi_dev_present() as well.
*/
struct acpi_device *
-acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
+acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv)
{
+ struct device *start = adev ? &adev->dev : NULL;
struct acpi_dev_match_info match = {};
struct device *dev;
@@ -865,9 +867,29 @@ acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
match.uid = uid;
match.hrv = hrv;
- dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
+ dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
return dev ? to_acpi_device(dev) : NULL;
}
+EXPORT_SYMBOL(acpi_dev_get_next_match_dev);
+
+/**
+ * acpi_dev_get_first_match_dev - Return the first match of ACPI device
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * Return the first match of ACPI device if a matching device was present
+ * at the moment of invocation, or NULL otherwise.
+ *
+ * The caller is responsible to call put_device() on the returned device.
+ *
+ * See additional information in acpi_dev_present() as well.
+ */
+struct acpi_device *
+acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
+{
+ return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
+}
EXPORT_SYMBOL(acpi_dev_get_first_match_dev);
/*
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 6d1879bf9440..02a716a0af5d 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -683,9 +683,16 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2);
+struct acpi_device *
+acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv);
struct acpi_device *
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv);
+#define for_each_acpi_dev_match(adev, hid, uid, hrv) \
+ for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv); \
+ adev; \
+ adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
+
static inline void acpi_dev_put(struct acpi_device *adev)
{
put_device(&adev->dev);
--
2.30.0
From fe93b62c3e4bd129fe8e47c935e637f06e0e412a Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 21:53:44 +0100
Subject: [PATCH] ipu3-cio2: Add functionality allowing software_node
connections to sensors on platforms designed for Windows
Currently on platforms designed for Windows, connections between CIO2 and
sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
driver to compensate by building software_node connections, parsing the
connection properties from the sensor's SSDB buffer.
Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 1 +
drivers/media/pci/intel/ipu3/Kconfig | 18 ++
drivers/media/pci/intel/ipu3/Makefile | 1 +
drivers/media/pci/intel/ipu3/cio2-bridge.c | 260 ++++++++++++++++++
drivers/media/pci/intel/ipu3/cio2-bridge.h | 108 ++++++++
drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 27 ++
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 6 +
7 files changed, 421 insertions(+)
create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 5a1c6e959aa8..a6924e3401e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8935,6 +8935,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
M: Yong Zhi <yong.zhi@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Bingbu Cao <bingbu.cao@intel.com>
+M: Dan Scally <djrscally@gmail.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
index 82d7f17e6a02..2b3350d042be 100644
--- a/drivers/media/pci/intel/ipu3/Kconfig
+++ b/drivers/media/pci/intel/ipu3/Kconfig
@@ -16,3 +16,21 @@ config VIDEO_IPU3_CIO2
Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
connected camera.
The module will be called ipu3-cio2.
+
+config CIO2_BRIDGE
+ bool "IPU3 CIO2 Sensors Bridge"
+ depends on VIDEO_IPU3_CIO2
+ help
+ This extension provides an API for the ipu3-cio2 driver to create
+ connections to cameras that are hidden in SSDB buffer in ACPI. It
+ can be used to enable support for cameras in detachable / hybrid
+ devices that ship with Windows.
+
+ Say Y here if your device is a detachable / hybrid laptop that comes
+ with Windows installed by the OEM, for example:
+
+ - Microsoft Surface models (except Surface Pro 3)
+ - The Lenovo Miix line (for example the 510, 520, 710 and 720)
+ - Dell 7285
+
+ If in doubt, say N here.
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index 429d516452e4..933777e6ea8a 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
ipu3-cio2-y += ipu3-cio2-main.o
+ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
new file mode 100644
index 000000000000..fd3f8ba07274
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/property.h>
+#include <media/v4l2-subdev.h>
+
+#include "cio2-bridge.h"
+
+/*
+ * Extend this array with ACPI Hardware ID's of devices known to be working.
+ * Do not add a HID for a sensor that is not actually supported.
+ */
+static const char * const cio2_supported_devices[] = {
+ "INT33BE",
+ "OVTI2680",
+};
+
+static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
+ void *data, u32 size)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret;
+
+ status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ obj = buffer.pointer;
+ if (!obj) {
+ dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
+ return -ENODEV;
+ }
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&adev->dev, "Not an ACPI buffer\n");
+ ret = -ENODEV;
+ goto out_free_buff;
+ }
+
+ if (obj->buffer.length > size) {
+ dev_err(&adev->dev, "Given buffer is too small\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ memcpy(data, obj->buffer.pointer, obj->buffer.length);
+ ret = obj->buffer.length;
+
+out_free_buff:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static void cio2_bridge_init_property_names(struct cio2_sensor *sensor)
+{
+ strcpy(sensor->prop_names.clock_frequency, "clock-frequency");
+ strcpy(sensor->prop_names.rotation, "rotation");
+ strcpy(sensor->prop_names.bus_type, "bus-type");
+ strcpy(sensor->prop_names.data_lanes, "data-lanes");
+ strcpy(sensor->prop_names.remote_endpoint, "remote-endpoint");
+}
+
+static void cio2_bridge_create_fwnode_properties(struct cio2_sensor *sensor)
+{
+ unsigned int i;
+
+ cio2_bridge_init_property_names(sensor);
+
+ for (i = 0; i < 4; i++)
+ sensor->data_lanes[i] = i + 1;
+
+ /*
+ * Can't use PROPERTY_ENTRY_REF because it creates a new variable to
+ * point to, which doesn't survive the function.
+ */
+ sensor->local_ref[0] = (struct software_node_ref_args){
+ .node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT]
+ };
+ sensor->remote_ref[0] = (struct software_node_ref_args){
+ .node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT]
+ };
+
+ sensor->dev_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.clock_frequency,
+ sensor->ssdb.mclkspeed);
+ sensor->dev_properties[1] = PROPERTY_ENTRY_U8(sensor->prop_names.rotation,
+ sensor->ssdb.degree);
+
+ sensor->ep_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.bus_type, 5);
+ sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes,
+ sensor->data_lanes,
+ sensor->ssdb.lanes);
+ sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint,
+ sensor->local_ref);
+
+ sensor->cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes,
+ sensor->data_lanes,
+ sensor->ssdb.lanes);
+ sensor->cio2_properties[1] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint,
+ sensor->remote_ref);
+}
+
+static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor)
+{
+ snprintf(sensor->node_names.remote_port, 6, "port%u", sensor->ssdb.link);
+ strcpy(sensor->node_names.port, "port0");
+ strcpy(sensor->node_names.endpoint, "endpoint0");
+}
+
+static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
+ struct cio2_sensor *sensor)
+{
+ struct software_node *nodes = sensor->swnodes;
+
+ cio2_bridge_init_swnode_names(sensor);
+
+ nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
+ sensor->dev_properties);
+ nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port,
+ &nodes[SWNODE_SENSOR_HID]);
+ nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint,
+ &nodes[SWNODE_SENSOR_PORT],
+ sensor->ep_properties);
+ nodes[SWNODE_CIO2_PORT] = NODE_PORT(sensor->node_names.remote_port,
+ &bridge->cio2_hid_node);
+ nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint,
+ &nodes[SWNODE_CIO2_PORT],
+ sensor->cio2_properties);
+}
+
+static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
+{
+ struct cio2_sensor *sensor;
+ unsigned int i;
+
+ for (i = 0; i < bridge->n_sensors; i++) {
+ sensor = &bridge->sensors[i];
+ software_node_unregister_nodes(sensor->swnodes);
+ acpi_dev_put(sensor->adev);
+ }
+}
+
+static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge)
+{
+ struct fwnode_handle *fwnode;
+ struct cio2_sensor *sensor;
+ struct acpi_device *adev;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(cio2_supported_devices); i++) {
+ const char *this_device = cio2_supported_devices[i];
+
+ for_each_acpi_dev_match(adev, this_device, NULL, -1) {
+ if (!adev || !(adev->status.present && adev->status.enabled))
+ continue;
+
+ sensor = &bridge->sensors[bridge->n_sensors];
+ sensor->adev = adev;
+ strscpy(sensor->name, this_device, sizeof(sensor->name));
+
+ ret = cio2_bridge_read_acpi_buffer(adev, "SSDB",
+ &sensor->ssdb,
+ sizeof(sensor->ssdb));
+ if (ret < 0)
+ goto err_put_adev;
+
+ if (sensor->ssdb.lanes > 4) {
+ dev_err(&adev->dev,
+ "Number of lanes in SSDB is invalid\n");
+ goto err_put_adev;
+ }
+
+ cio2_bridge_create_fwnode_properties(sensor);
+ cio2_bridge_create_connection_swnodes(bridge, sensor);
+
+ ret = software_node_register_nodes(sensor->swnodes);
+ if (ret)
+ goto err_put_adev;
+
+ fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
+ if (!fwnode) {
+ ret = -ENODEV;
+ goto err_free_swnodes;
+ }
+
+ adev->fwnode.secondary = fwnode;
+
+ dev_info(&bridge->cio2->dev,
+ "Found supported sensor %s\n",
+ acpi_dev_name(adev));
+
+ bridge->n_sensors++;
+ }
+ }
+
+ return ret;
+
+err_free_swnodes:
+ software_node_unregister_nodes(sensor->swnodes);
+err_put_adev:
+ acpi_dev_put(sensor->adev);
+
+ return ret;
+}
+
+int cio2_bridge_init(struct pci_dev *cio2)
+{
+ struct device *dev = &cio2->dev;
+ struct fwnode_handle *fwnode;
+ struct cio2_bridge *bridge;
+ int ret;
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ strscpy(bridge->cio2_node_name, CIO2_HID, sizeof(bridge->cio2_node_name));
+ bridge->cio2_hid_node = (const struct software_node){ bridge->cio2_node_name };
+ bridge->cio2 = pci_dev_get(cio2);
+
+ ret = software_node_register(&bridge->cio2_hid_node);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register the CIO2 HID node\n");
+ goto err_put_cio2;
+ }
+
+ ret = cio2_bridge_connect_sensors(bridge);
+ if (ret || bridge->n_sensors == 0)
+ goto err_unregister_cio2;
+
+ dev_info(dev, "Connected %d cameras\n", bridge->n_sensors);
+
+ fwnode = software_node_fwnode(&bridge->cio2_hid_node);
+ if (!fwnode) {
+ dev_err(dev, "Error getting fwnode from cio2 software_node\n");
+ ret = -ENODEV;
+ goto err_unregister_sensors;
+ }
+
+ set_secondary_fwnode(dev, fwnode);
+
+ return 0;
+
+err_unregister_sensors:
+ cio2_bridge_unregister_sensors(bridge);
+err_unregister_cio2:
+ software_node_unregister(&bridge->cio2_hid_node);
+err_put_cio2:
+ pci_dev_put(bridge->cio2);
+
+ kfree(bridge);
+ return ret;
+}
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
new file mode 100644
index 000000000000..96f5c8a12be0
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+#ifndef __CIO2_BRIDGE_H
+#define __CIO2_BRIDGE_H
+
+#include <linux/property.h>
+
+#define CIO2_HID "INT343E"
+#define CIO2_NUM_PORTS 4
+
+#define NODE_SENSOR(_HID, _PROPS) \
+ ((const struct software_node) { \
+ .name = _HID, \
+ .properties = _PROPS, \
+ })
+
+#define NODE_PORT(_PORT, _SENSOR_NODE) \
+ ((const struct software_node) { \
+ _PORT, \
+ _SENSOR_NODE, \
+ })
+
+#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \
+ ((const struct software_node) { \
+ _EP, \
+ _PORT, \
+ _PROPS, \
+ })
+
+enum cio2_sensor_swnodes {
+ SWNODE_SENSOR_HID,
+ SWNODE_SENSOR_PORT,
+ SWNODE_SENSOR_ENDPOINT,
+ SWNODE_CIO2_PORT,
+ SWNODE_CIO2_ENDPOINT,
+ NR_OF_SENSOR_SWNODES
+};
+
+/* Data representation as it is in ACPI SSDB buffer */
+struct cio2_sensor_ssdb {
+ u8 version;
+ u8 sku;
+ u8 guid_csi2[16];
+ u8 devfunction;
+ u8 bus;
+ u32 dphylinkenfuses;
+ u32 clockdiv;
+ u8 link;
+ u8 lanes;
+ u32 csiparams[10];
+ u32 maxlanespeed;
+ u8 sensorcalibfileidx;
+ u8 sensorcalibfileidxInMBZ[3];
+ u8 romtype;
+ u8 vcmtype;
+ u8 platforminfo;
+ u8 platformsubinfo;
+ u8 flash;
+ u8 privacyled;
+ u8 degree;
+ u8 mipilinkdefined;
+ u32 mclkspeed;
+ u8 controllogicid;
+ u8 reserved1[3];
+ u8 mclkport;
+ u8 reserved2[13];
+} __packed__;
+
+struct cio2_property_names {
+ char clock_frequency[16];
+ char rotation[9];
+ char bus_type[9];
+ char data_lanes[11];
+ char remote_endpoint[16];
+};
+
+struct cio2_node_names {
+ char port[6];
+ char endpoint[10];
+ char remote_port[6];
+};
+
+struct cio2_sensor {
+ char name[ACPI_ID_LEN];
+ struct acpi_device *adev;
+
+ struct software_node swnodes[6];
+ struct cio2_node_names node_names;
+
+ u32 data_lanes[4];
+ struct cio2_sensor_ssdb ssdb;
+ struct cio2_property_names prop_names;
+ struct property_entry ep_properties[4];
+ struct property_entry dev_properties[3];
+ struct property_entry cio2_properties[3];
+ struct software_node_ref_args local_ref[1];
+ struct software_node_ref_args remote_ref[1];
+};
+
+struct cio2_bridge {
+ struct pci_dev *cio2;
+ char cio2_node_name[ACPI_ID_LEN];
+ struct software_node cio2_hid_node;
+ unsigned int n_sensors;
+ struct cio2_sensor sensors[CIO2_NUM_PORTS];
+};
+
+#endif
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index c557d189200b..9f29af0c0941 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1712,6 +1712,22 @@ static void cio2_queues_exit(struct cio2_device *cio2)
cio2_queue_exit(cio2, &cio2->queue[i]);
}
+static bool cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *endpoint;
+
+ if (IS_ERR_OR_NULL(fwnode))
+ return false;
+
+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (endpoint) {
+ fwnode_handle_put(endpoint);
+ return true;
+ }
+
+ return cio2_check_fwnode_graph(fwnode->secondary);
+}
+
/**************** PCI interface ****************/
static int cio2_pci_probe(struct pci_dev *pci_dev,
@@ -1725,6 +1741,17 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
return -ENOMEM;
cio2->pci_dev = pci_dev;
+ /*
+ * On some platforms no connections to sensors are defined in firmware,
+ * if the device has no endpoints then we can try to build those as
+ * software_nodes parsed from SSDB.
+ */
+ if (!cio2_check_fwnode_graph(dev_fwnode(&pci_dev->dev))) {
+ r = cio2_bridge_init(pci_dev);
+ if (r)
+ return r;
+ }
+
r = pcim_enable_device(pci_dev);
if (r) {
dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index ccf0b85ae36f..520a27c9cdad 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -437,4 +437,10 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq)
return container_of(vq, struct cio2_queue, vbq);
}
+#if IS_ENABLED(CONFIG_CIO2_BRIDGE)
+int cio2_bridge_init(struct pci_dev *cio2);
+#else
+int cio2_bridge_init(struct pci_dev *cio2) { return 0; }
+#endif
+
#endif
--
2.30.0
From d47ca9659358677aea827a0f403cfc7ccf9ccf6d Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 26 Nov 2020 21:12:41 +0000
Subject: [PATCH] acpi: utils: Add function to fetch dependent acpi_devices
ACPI devices declare themselves dependent on other devices via the _DEP
buffer. Fetching the dependee from dependent is a matter of parsing
_DEP, but currently there's no method to fetch dependent from dependee.
Add one, so we can parse sensors dependent on a PMIC from the PMIC's
acpi_driver.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/utils.c | 68 +++++++++++++++++++++++++++++++++++++++++
include/acpi/acpi_bus.h | 2 ++
2 files changed, 70 insertions(+)
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index c177165c8db2..7099529121db 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -807,6 +807,52 @@ static int acpi_dev_match_cb(struct device *dev, const void *data)
return hrv == match->hrv;
}
+static int acpi_dev_match_by_dep(struct device *dev, const void *data)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+ const struct acpi_device *dependee = data;
+ struct acpi_handle_list dep_handles;
+ struct acpi_device *candidate;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned int i;
+ int ret;
+
+ handle = adev->handle;
+
+ if (!acpi_has_method(handle, "_DEP"))
+ return 0;
+
+ status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_handles);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ for (i = 0; i < dep_handles.count; i++) {
+ struct acpi_device_info *info;
+
+ status = acpi_get_object_info(dep_handles.handles[i], &info);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ if (info->valid & ACPI_VALID_HID) {
+ ret = acpi_bus_get_device(dep_handles.handles[i], &candidate);
+ if (ret || !candidate) {
+ kfree(info);
+ continue;
+ }
+
+ if (candidate == dependee) {
+ acpi_dev_put(candidate);
+ kfree(info);
+ return 1;
+ }
+
+ kfree(info);
+ }
+ }
+ return 0;
+}
+
/**
* acpi_dev_present - Detect that a given ACPI device is present
* @hid: Hardware ID of the device.
@@ -842,6 +888,28 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
}
EXPORT_SYMBOL(acpi_dev_present);
+/**
+ * acpi_dev_get_next_dep_dev - Return next ACPI device dependent on input dev
+ * @adev: Pointer to the dependee device
+ * @prev: Pointer to the previous dependent device (or NULL for first match)
+ *
+ * Return the next ACPI device which declares itself dependent on @adev in
+ * the _DEP buffer.
+ *
+ * The caller is responsible to call put_device() on the returned device.
+ */
+struct acpi_device *acpi_dev_get_next_dep_dev(struct acpi_device *adev,
+ struct acpi_device *prev)
+{
+ struct device *start = prev ? &prev->dev : NULL;
+ struct device *dev;
+
+ dev = bus_find_device(&acpi_bus_type, start, adev, acpi_dev_match_by_dep);
+
+ return dev ? to_acpi_device(dev) : NULL;
+}
+EXPORT_SYMBOL(acpi_dev_get_next_dep_dev);
+
/**
* acpi_dev_get_next_match_dev - Return the next match of ACPI device
* @adev: Pointer to the previous acpi_device matching this hid, uid and hrv
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 02a716a0af5d..33deb22294f2 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -683,6 +683,8 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2);
+struct acpi_device *
+acpi_dev_get_next_dep_dev(struct acpi_device *adev, struct acpi_device *prev);
struct acpi_device *
acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv);
struct acpi_device *
--
2.30.0
From 6ed8bea5378b79afcf7fe349b3fc90a794eb92a4 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 16 Nov 2020 21:38:49 +0000
Subject: [PATCH] i2c: i2c-core-acpi: Add i2c_acpi_dev_name()
Some places in the kernel allow users to map resources to a device
using device name (for example, gpiod_lookup_table). Currently
this involves waiting for the i2c_client to have been registered so we
can use dev_name(&client->dev). Adding this function means that we can
achieve the same thing without having to wait to the i2c device.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/i2c/i2c-core-acpi.c | 15 +++++++++++++++
include/linux/i2c.h | 5 +++++
2 files changed, 20 insertions(+)
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index aed579942436..a48e20a806c5 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -497,6 +497,21 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
}
EXPORT_SYMBOL_GPL(i2c_acpi_new_device);
+/**
+ * i2c_acpi_dev_name - Construct i2c device name for devs sourced from ACPI
+ * @adev: ACPI device to construct the name for
+ *
+ * Prefixes "i2c-" to the ACPI device name, for use in i2c_dev_set_name() and
+ * also anywhere else in the kernel that needs to refer to an i2c device by
+ * name but before they have been instantiated.
+ */
+char *i2c_acpi_dev_name(struct acpi_device *adev)
+{
+ return devm_kasprintf(&adev->dev, GFP_KERNEL, "i2c-%s",
+ acpi_dev_name(adev));
+}
+EXPORT_SYMBOL_GPL(i2c_acpi_dev_name);
+
#ifdef CONFIG_ACPI_I2C_OPREGION
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
u8 cmd, u8 *data, u8 data_len)
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 56622658b215..ab0e505b2ca6 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -995,6 +995,7 @@ bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
u32 i2c_acpi_find_bus_speed(struct device *dev);
struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
struct i2c_board_info *info);
+char *i2c_acpi_dev_name(struct acpi_device *adev);
struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle);
#else
static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
@@ -1011,6 +1012,10 @@ static inline struct i2c_client *i2c_acpi_new_device(struct device *dev,
{
return ERR_PTR(-ENODEV);
}
+static inline char *i2c_acpi_dev_name(struct acpi_device *adev)
+{
+ return NULL;
+}
static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle)
{
return NULL;
--
2.30.0
From ede2c404c07285093e1ee9bca9cf354f1df565c8 Mon Sep 17 00:00:00 2001
From: Dan Scally <djrscally@gmail.com>
Date: Mon, 16 Nov 2020 21:45:22 +0000
Subject: [PATCH] i2c: i2c-core-base: Use the new i2c_acpi_dev_name() in
i2c_set_dev_name()
To make sure the new i2c_acpi_dev_name() always reflects the name of i2c
devices sourced from ACPI, use it in i2c_set_dev_name().
Signed-off-by: Dan Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/i2c/i2c-core-base.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 573b5da145d1..a6d4ceb01077 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -814,7 +814,7 @@ static void i2c_dev_set_name(struct i2c_adapter *adap,
}
if (adev) {
- dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
+ dev_set_name(&client->dev, i2c_acpi_dev_name(adev));
return;
}
--
2.30.0
From dcad0ec65ef469c373bfe3083f39e1450d038df3 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 16 Nov 2020 00:16:56 +0000
Subject: [PATCH] gpio: gpiolib-acpi: Export acpi_get_gpiod()
I need to be able to translate GPIO resources in an acpi_device's _CRS
into gpio_descs. Those are represented in _CRS as a pathname to a GPIO
device plus the pin's index number: this function is perfect for that
purpose.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/gpio/gpiolib-acpi.c | 3 ++-
include/linux/acpi.h | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 834a12f3219e..cfadbc263475 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -111,7 +111,7 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
* controller does not have GPIO chip registered at the moment. This is to
* support probe deferral.
*/
-static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+struct gpio_desc *acpi_get_gpiod(char *path, int pin)
{
struct gpio_chip *chip;
acpi_handle handle;
@@ -127,6 +127,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
return gpiochip_get_desc(chip, pin);
}
+EXPORT_SYMBOL_GPL(acpi_get_gpiod);
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 39263c6b52e1..737115a93138 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1066,6 +1066,7 @@ void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const c
bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
struct acpi_resource_gpio **agpio);
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index);
+struct gpio_desc *acpi_get_gpiod(char *path, int pin);
#else
static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
struct acpi_resource_gpio **agpio)
@@ -1076,6 +1077,10 @@ static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{
return -ENXIO;
}
+struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+{
+ return NULL;
+}
#endif
/* Device properties */
--
2.30.0
From 3216ecb9f47705838a2d9ba67ec9c56058c81839 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 27 Nov 2020 10:18:10 +0000
Subject: [PATCH] ipu3: Add driver for dummy INT3472 ACPI device
On platforms where ACPI is designed for use with Windows, resources
that are intended to be consumed by sensor devices are sometimes in
the _CRS of a dummy INT3472 device upon which the sensor depends. This
driver binds to the dummy acpi device (which does not represent a
physical PMIC) and maps them into GPIO lines and regulators for use by
the sensor device instead.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 7 +
drivers/media/pci/intel/ipu3/Kconfig | 14 +
drivers/media/pci/intel/ipu3/Makefile | 1 +
drivers/media/pci/intel/ipu3/int3472.c | 381 +++++++++++++++++++++++++
drivers/media/pci/intel/ipu3/int3472.h | 96 +++++++
5 files changed, 499 insertions(+)
create mode 100644 drivers/media/pci/intel/ipu3/int3472.c
create mode 100644 drivers/media/pci/intel/ipu3/int3472.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a6924e3401e8..b5bd52b4dae3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8761,6 +8761,13 @@ L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/inside-secure/
+INT3472 ACPI DEVICE DRIVER
+M: Daniel Scally <djrscally@gmail.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/pci/intel/ipu3/int3472.c
+F: drivers/media/pci/intel/ipu3/int3472.h
+
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
M: Mimi Zohar <zohar@linux.ibm.com>
M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
index 2b3350d042be..9dd3b280f821 100644
--- a/drivers/media/pci/intel/ipu3/Kconfig
+++ b/drivers/media/pci/intel/ipu3/Kconfig
@@ -34,3 +34,17 @@ config CIO2_BRIDGE
- Dell 7285
If in doubt, say N here.
+
+config INT3472
+ tristate "INT3472 Dummy ACPI Device Driver"
+ depends on VIDEO_IPU3_CIO2
+ depends on ACPI && REGULATOR && GPIOLIB
+ help
+ This module provides an ACPI driver for INT3472 devices that do not
+ represent an actual physical tps68470 device.
+
+ Say Y here if your device is a detachable / hybrid laptop that comes
+ with Windows installed by the OEM.
+ The module will be called int3472.
+
+ If in doubt, say N here.
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index 933777e6ea8a..2285947b2bd2 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
+obj-$(CONFIG_INT3472) += int3472.o
ipu3-cio2-y += ipu3-cio2-main.o
ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
diff --git a/drivers/media/pci/intel/ipu3/int3472.c b/drivers/media/pci/intel/ipu3/int3472.c
new file mode 100644
index 000000000000..9fab8dd2ac1b
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/int3472.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+
+#include "int3472.h"
+
+/*
+ * The regulators have to have .ops to be valid, but the only ops we actually
+ * support are .enable and .disable which are handled via .ena_gpiod. Pass an
+ * empty struct to clear the check without lying about capabilities.
+ */
+static const struct regulator_ops int3472_gpio_regulator_ops = { 0 };
+
+static int int3472_map_gpio_to_sensor(struct int3472_device *int3472,
+ struct acpi_resource *ares, char *func)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct gpiod_lookup table_entry;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_status status;
+ int ret;
+
+ /* Make sure we don't overflow, and leave room for a terminator */
+ if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
+ dev_warn(&int3472->sensor->dev, "Too many GPIOs mapped\n");
+ return -EINVAL;
+ }
+
+ /* Fetch ACPI handle for the GPIO chip */
+ status = acpi_get_handle(NULL, path, &handle);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ ret = acpi_bus_get_device(handle, &adev);
+ if (ret)
+ return -ENODEV;
+
+ table_entry = (struct gpiod_lookup)GPIO_LOOKUP_IDX(acpi_dev_name(adev),
+ ares->data.gpio.pin_table[0],
+ func, 0, GPIO_ACTIVE_HIGH);
+
+ memcpy(&int3472->gpios.table[int3472->n_sensor_gpios], &table_entry,
+ sizeof(table_entry));
+ int3472->n_sensor_gpios++;
+
+ return 0;
+}
+
+static struct int3472_sensor_regulator_map *
+int3472_get_sensor_supply_map(struct int3472_device *int3472)
+{
+ struct int3472_sensor_regulator_map *ret;
+ union acpi_object *obj;
+ unsigned int i;
+
+ /*
+ * Sensor modules seem to be identified by a unique string. We use that
+ * to make sure we pass the right device and supply names to the new
+ * regulator's consumer_supplies
+ */
+ obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
+ &cio2_sensor_module_guid, 0x00,
+ 0x01, NULL, ACPI_TYPE_STRING);
+
+ if (!obj) {
+ dev_err(&int3472->sensor->dev,
+ "Failed to get sensor module string from _DSM\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (obj->string.type != ACPI_TYPE_STRING) {
+ dev_err(&int3472->sensor->dev,
+ "Sensor _DSM returned a non-string value\n");
+ ret = ERR_PTR(-EINVAL);
+ goto out_free_obj;
+ }
+
+ ret = ERR_PTR(-ENODEV);
+ for (i = 0; i < ARRAY_SIZE(int3472_sensor_regulator_maps); i++) {
+ if (!strcmp(int3472_sensor_regulator_maps[i].sensor_module_name,
+ obj->string.pointer)) {
+ ret = &int3472_sensor_regulator_maps[i];
+ goto out_free_obj;
+ }
+ }
+
+out_free_obj:
+ ACPI_FREE(obj);
+ return ret;
+}
+
+static int int3472_register_regulator(struct int3472_device *int3472,
+ struct acpi_resource *ares)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct int3472_sensor_regulator_map *regulator_map;
+ struct regulator_init_data init_data = { };
+ struct int3472_gpio_regulator *regulator;
+ struct regulator_config cfg = { };
+ int ret;
+
+ /*
+ * We lookup supply names from machine specific tables, based on a
+ * unique identifier in the sensor's _DSM
+ */
+ regulator_map = int3472_get_sensor_supply_map(int3472);
+ if (IS_ERR_OR_NULL(regulator_map)) {
+ dev_err(&int3472->sensor->dev,
+ "Found no supplies defined for this sensor\n");
+ return PTR_ERR(regulator_map);
+ }
+
+ if (int3472->n_regulators >= regulator_map->n_supplies) {
+ dev_err(&int3472->sensor->dev,
+ "All known supplies are already mapped\n");
+ return -EINVAL;
+ }
+
+ init_data.supply_regulator = NULL;
+ init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
+ init_data.num_consumer_supplies = 1;
+ init_data.consumer_supplies = &regulator_map->supplies[int3472->n_regulators];
+
+ regulator = kmalloc(sizeof(*regulator), GFP_KERNEL);
+ if (!regulator)
+ return -ENOMEM;
+
+ snprintf(regulator->regulator_name, GPIO_REGULATOR_NAME_LENGTH,
+ "gpio-regulator-%d", int3472->n_regulators);
+ snprintf(regulator->supply_name, GPIO_REGULATOR_SUPPLY_NAME_LENGTH,
+ "supply-%d", int3472->n_regulators);
+
+ regulator->rdesc = INT3472_REGULATOR(regulator->regulator_name,
+ regulator->supply_name,
+ int3472->n_regulators,
+ &int3472_gpio_regulator_ops);
+
+ regulator->gpio = acpi_get_gpiod(path, ares->data.gpio.pin_table[0]);
+ if (IS_ERR(regulator->gpio)) {
+ ret = PTR_ERR(regulator->gpio);
+ goto err_free_regulator;
+ }
+
+ cfg.dev = &int3472->adev->dev;
+ cfg.init_data = &init_data;
+ cfg.ena_gpiod = regulator->gpio;
+
+ regulator->rdev = regulator_register(&regulator->rdesc, &cfg);
+ if (IS_ERR(regulator->rdev)) {
+ ret = PTR_ERR(regulator->rdev);
+ goto err_free_gpio;
+ }
+
+ list_add(&regulator->list, &int3472->regulators);
+ int3472->n_regulators++;
+
+ return 0;
+
+err_free_gpio:
+ gpiod_put(regulator->gpio);
+err_free_regulator:
+ kfree(regulator);
+
+ return ret;
+}
+
+static int int3472_handle_gpio_resources(struct acpi_resource *ares,
+ void *data)
+{
+ struct int3472_device *int3472 = data;
+ union acpi_object *obj;
+ int ret = 0;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO ||
+ ares->data.gpio.connection_type != ACPI_RESOURCE_GPIO_TYPE_IO)
+ return EINVAL; /* Deliberately positive */
+
+ /*
+ * n_gpios + 2 because the index of this _DSM function is 1-based and
+ * the first function is just a count.
+ */
+ obj = acpi_evaluate_dsm_typed(int3472->adev->handle,
+ &int3472_gpio_guid, 0x00,
+ int3472->n_gpios + 2,
+ NULL, ACPI_TYPE_INTEGER);
+
+ if (!obj) {
+ dev_warn(&int3472->adev->dev,
+ "No _DSM entry for this GPIO pin\n");
+ return ENODEV;
+ }
+
+ switch (obj->integer.value & 0xff) { /* low byte holds type data */
+ case 0x00: /* Purpose unclear, possibly a reset GPIO pin */
+ ret = int3472_map_gpio_to_sensor(int3472, ares, "reset");
+ if (ret)
+ dev_warn(&int3472->adev->dev,
+ "Failed to map reset pin to sensor\n");
+
+ break;
+ case 0x01: /* Power regulators (we think) */
+ case 0x0c:
+ ret = int3472_register_regulator(int3472, ares);
+ if (ret)
+ dev_warn(&int3472->adev->dev,
+ "Failed to map regulator to sensor\n");
+
+ break;
+ case 0x0b: /* Power regulators, but to a device separate to sensor */
+ ret = int3472_register_regulator(int3472, ares);
+ if (ret)
+ dev_warn(&int3472->adev->dev,
+ "Failed to map regulator to sensor\n");
+
+ break;
+ case 0x0d: /* Indicator LEDs */
+ ret = int3472_map_gpio_to_sensor(int3472, ares, "indicator-led");
+ if (ret)
+ dev_warn(&int3472->adev->dev,
+ "Failed to map indicator led to sensor\n");
+
+ break;
+ default:
+ /* if we've gotten here, we're not sure what they are yet */
+ dev_warn(&int3472->adev->dev,
+ "GPIO type 0x%llx unknown; the sensor may not work\n",
+ (obj->integer.value & 0xff));
+ ret = EINVAL;
+ }
+
+ int3472->n_gpios++;
+ ACPI_FREE(obj);
+ return abs(ret);
+}
+
+static void int3472_parse_crs(struct int3472_device *int3472)
+{
+ struct list_head resource_list;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ acpi_dev_get_resources(int3472->adev, &resource_list,
+ int3472_handle_gpio_resources, int3472);
+
+ acpi_dev_free_resource_list(&resource_list);
+ gpiod_add_lookup_table(&int3472->gpios);
+}
+
+static int int3472_add(struct acpi_device *adev)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct int3472_device *int3472;
+ struct int3472_cldb cldb;
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ /*
+ * This driver is only intended to support "dummy" INT3472 devices
+ * which appear in ACPI designed for Windows. These are distinguishable
+ * from INT3472 entries representing an actual tps68470 PMIC through
+ * the presence of a CLDB buffer with a particular value set.
+ */
+ status = acpi_evaluate_object(adev->handle, "CLDB", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ obj = buffer.pointer;
+ if (!obj) {
+ dev_err(&adev->dev, "ACPI device has no CLDB object\n");
+ return -ENODEV;
+ }
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&adev->dev, "CLDB object is not an ACPI buffer\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ if (obj->buffer.length > sizeof(cldb)) {
+ dev_err(&adev->dev, "The CLDB buffer is too large\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ memcpy(&cldb, obj->buffer.pointer, obj->buffer.length);
+
+ /*
+ * control_logic_type = 1 indicates this is a dummy INT3472 device of
+ * the kind we're looking for. If any other value then we shouldn't try
+ * to handle it
+ */
+ if (cldb.control_logic_type != 1) {
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ /* Space for 4 GPIOs - one more than we've seen so far plus a null */
+ int3472 = kzalloc(sizeof(*int3472) +
+ ((INT3472_MAX_SENSOR_GPIOS + 1) * sizeof(struct gpiod_lookup)),
+ GFP_KERNEL);
+ if (!int3472) {
+ ret = -ENOMEM;
+ goto out_free_buff;
+ }
+
+ int3472->adev = adev;
+ adev->driver_data = int3472;
+
+ int3472->sensor = acpi_dev_get_next_dep_dev(adev, NULL);
+ if (!int3472->sensor) {
+ dev_err(&adev->dev,
+ "This INT3472 entry seems to have no dependents.\n");
+ ret = -ENODEV;
+ goto out_free_int3472;
+ }
+
+ int3472->gpios.dev_id = i2c_acpi_dev_name(int3472->sensor);
+
+ INIT_LIST_HEAD(&int3472->regulators);
+
+ int3472_parse_crs(int3472);
+
+ goto out_free_buff;
+
+out_free_int3472:
+ kfree(int3472);
+out_free_buff:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static int int3472_remove(struct acpi_device *adev)
+{
+ struct int3472_gpio_regulator *reg;
+ struct int3472_device *int3472;
+
+ int3472 = acpi_driver_data(adev);
+
+ acpi_dev_put(int3472->sensor);
+ gpiod_remove_lookup_table(&int3472->gpios);
+
+ list_for_each_entry(reg, &int3472->regulators, list) {
+ gpiod_put(reg->gpio);
+ regulator_unregister(reg->rdev);
+ }
+
+ kfree(int3472);
+
+ return 0;
+}
+
+static const struct acpi_device_id int3472_device_id[] = {
+ { "INT3472", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, int3472_device_id);
+
+static struct acpi_driver int3472_driver = {
+ .name = "int3472",
+ .ids = int3472_device_id,
+ .ops = {
+ .add = int3472_add,
+ .remove = int3472_remove,
+ },
+ .owner = THIS_MODULE,
+};
+
+module_acpi_driver(int3472_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Dan Scally <djrscally@gmail.com>");
+MODULE_DESCRIPTION("ACPI Driver for Discrete type INT3472 ACPI Devices");
diff --git a/drivers/media/pci/intel/ipu3/int3472.h b/drivers/media/pci/intel/ipu3/int3472.h
new file mode 100644
index 000000000000..6964726e8e1f
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/int3472.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+#include <linux/regulator/machine.h>
+
+#define INT3472_MAX_SENSOR_GPIOS 3
+#define GPIO_REGULATOR_NAME_LENGTH 17
+#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
+
+#define INT3472_REGULATOR(_NAME, _SUPPLY, _ID, _OPS) \
+ ((const struct regulator_desc) { \
+ .name = _NAME, \
+ .supply_name = _SUPPLY, \
+ .id = _ID, \
+ .type = REGULATOR_VOLTAGE, \
+ .ops = _OPS, \
+ .owner = THIS_MODULE, \
+ })
+
+const guid_t int3472_gpio_guid = GUID_INIT(0x79234640, 0x9e10, 0x4fea,
+ 0xa5, 0xc1, 0xb5, 0xaa, 0x8b,
+ 0x19, 0x75, 0x6f);
+
+const guid_t cio2_sensor_module_guid = GUID_INIT(0x822ace8f, 0x2814, 0x4174,
+ 0xa5, 0x6b, 0x5f, 0x02, 0x9f,
+ 0xe0, 0x79, 0xee);
+
+struct int3472_cldb {
+ u8 version;
+ /*
+ * control logic type
+ * 0: UNKNOWN
+ * 1: DISCRETE(CRD-D)
+ * 2: PMIC TPS68470
+ * 3: PMIC uP6641
+ */
+ u8 control_logic_type;
+ u8 control_logic_id;
+ u8 sensor_card_sku;
+ u8 reserved[28];
+};
+
+struct int3472_device {
+ struct acpi_device *adev;
+ struct acpi_device *sensor;
+
+ unsigned int n_gpios; /* how many GPIOs have we seen */
+
+ unsigned int n_regulators;
+ struct list_head regulators;
+
+ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */
+ struct gpiod_lookup_table gpios;
+};
+
+struct int3472_gpio_regulator {
+ char regulator_name[GPIO_REGULATOR_NAME_LENGTH];
+ char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH];
+ struct gpio_desc *gpio;
+ struct regulator_dev *rdev;
+ struct regulator_desc rdesc;
+ struct list_head list;
+};
+
+struct int3472_sensor_regulator_map {
+ char *sensor_module_name;
+ unsigned int n_supplies;
+ struct regulator_consumer_supply *supplies;
+};
+
+/*
+ * Here follows platform specific mapping information that we can pass to
+ * regulator_init_data when we register our regulators. They're just mapped
+ * via index, I.E. the first regulator pin that the code finds for the
+ * i2c-OVTI2680:00 device is avdd, the second is dovdd and so on.
+ */
+
+static struct regulator_consumer_supply miix_510_ov2680[] = {
+ { "i2c-OVTI2680:00", "avdd" },
+ { "i2c-OVTI2680:00", "dovdd" },
+};
+
+static struct regulator_consumer_supply surface_go2_ov5693[] = {
+ { "i2c-INT33BE:00", "avdd" },
+ { "i2c-INT33BE:00", "dovdd" },
+};
+
+static struct regulator_consumer_supply surface_book_ov5693[] = {
+ { "i2c-INT33BE:00", "avdd" },
+ { "i2c-INT33BE:00", "dovdd" },
+};
+
+static struct int3472_sensor_regulator_map int3472_sensor_regulator_maps[] = {
+ { "GNDF140809R", 2, miix_510_ov2680 },
+ { "YHCU", 2, surface_go2_ov5693 },
+ { "MSHW0070", 2, surface_book_ov5693 },
+};
--
2.30.0
From 240c902de0691faccd3a3c636500857c59a2cac3 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Thu, 10 Dec 2020 17:45:49 +0100
Subject: [PATCH] ipu3: int3472: Add missing IDs for Surface Book 1
Patchset: cameras
---
drivers/media/pci/intel/ipu3/int3472.h | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/int3472.h b/drivers/media/pci/intel/ipu3/int3472.h
index 6964726e8e1f..d9f74a0c94c9 100644
--- a/drivers/media/pci/intel/ipu3/int3472.h
+++ b/drivers/media/pci/intel/ipu3/int3472.h
@@ -84,13 +84,25 @@ static struct regulator_consumer_supply surface_go2_ov5693[] = {
{ "i2c-INT33BE:00", "dovdd" },
};
-static struct regulator_consumer_supply surface_book_ov5693[] = {
+static struct regulator_consumer_supply surface_ov5693[] = {
{ "i2c-INT33BE:00", "avdd" },
{ "i2c-INT33BE:00", "dovdd" },
};
+static struct regulator_consumer_supply surface_ov8865[] = {
+ { "i2c-INT347A:00", "avdd" },
+ { "i2c-INT347A:00", "dovdd" },
+};
+
+static struct regulator_consumer_supply surface_ov7251[] = {
+ { "i2c-INT347E:00", "avdd" },
+ { "i2c-INT347E:00", "dovdd" },
+};
+
static struct int3472_sensor_regulator_map int3472_sensor_regulator_maps[] = {
{ "GNDF140809R", 2, miix_510_ov2680 },
{ "YHCU", 2, surface_go2_ov5693 },
- { "MSHW0070", 2, surface_book_ov5693 },
+ { "MSHW0070", 2, surface_ov5693 }, /* Surface Book 1/Pro 4, front */
+ { "MSHW0071", 2, surface_ov8865 }, /* Surface Book 1/Pro 4, rear */
+ { "MSHW0072", 2, surface_ov7251 }, /* Surface Book 1/Pro 4, IR */
};
--
2.30.0
From 5453a46fbf2a7ef478fe32f3e6a9e2baedb4f387 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Thu, 10 Dec 2020 17:46:22 +0100
Subject: [PATCH] ipu3: int3472: Add IDs for Surface Book 2
Patchset: cameras
---
drivers/media/pci/intel/ipu3/int3472.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/media/pci/intel/ipu3/int3472.h b/drivers/media/pci/intel/ipu3/int3472.h
index d9f74a0c94c9..242cc82b9e95 100644
--- a/drivers/media/pci/intel/ipu3/int3472.h
+++ b/drivers/media/pci/intel/ipu3/int3472.h
@@ -105,4 +105,10 @@ static struct int3472_sensor_regulator_map int3472_sensor_regulator_maps[] = {
{ "MSHW0070", 2, surface_ov5693 }, /* Surface Book 1/Pro 4, front */
{ "MSHW0071", 2, surface_ov8865 }, /* Surface Book 1/Pro 4, rear */
{ "MSHW0072", 2, surface_ov7251 }, /* Surface Book 1/Pro 4, IR */
+ { "MSHW0140", 2, surface_ov5693 }, /* Surface Book 2 (13"), front */
+ { "MSHW0141", 2, surface_ov8865 }, /* Surface Book 2 (13"), rear */
+ { "MSHW0142", 2, surface_ov7251 }, /* Surface Book 2 (13"), IR */
+ { "MSHW0150", 2, surface_ov5693 }, /* Surface Book 2 (15"), front */
+ { "MSHW0151", 2, surface_ov8865 }, /* Surface Book 2 (15"), rear */
+ { "MSHW0152", 2, surface_ov7251 }, /* Surface Book 2 (15"), IR */
};
--
2.30.0
From 8788286d80675ef68270ab3298d6cc390ab3687c Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 15 Dec 2020 17:43:00 +0100
Subject: [PATCH] ipu3: int3472: Add IDs for Surface Pro 5 and Pro 6
Patchset: cameras
---
drivers/media/pci/intel/ipu3/int3472.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/media/pci/intel/ipu3/int3472.h b/drivers/media/pci/intel/ipu3/int3472.h
index 242cc82b9e95..1d2e9cea54f4 100644
--- a/drivers/media/pci/intel/ipu3/int3472.h
+++ b/drivers/media/pci/intel/ipu3/int3472.h
@@ -105,6 +105,9 @@ static struct int3472_sensor_regulator_map int3472_sensor_regulator_maps[] = {
{ "MSHW0070", 2, surface_ov5693 }, /* Surface Book 1/Pro 4, front */
{ "MSHW0071", 2, surface_ov8865 }, /* Surface Book 1/Pro 4, rear */
{ "MSHW0072", 2, surface_ov7251 }, /* Surface Book 1/Pro 4, IR */
+ { "MSHW0120", 2, surface_ov5693 }, /* Surface Pro 5/Pro 6, front */
+ { "MSHW0121", 2, surface_ov8865 }, /* Surface Pro 5/Pro 6, rear */
+ { "MSHW0122", 2, surface_ov7251 }, /* Surface Pro 5/Pro 6, IR */
{ "MSHW0140", 2, surface_ov5693 }, /* Surface Book 2 (13"), front */
{ "MSHW0141", 2, surface_ov8865 }, /* Surface Book 2 (13"), rear */
{ "MSHW0142", 2, surface_ov7251 }, /* Surface Book 2 (13"), IR */
--
2.30.0
From 6b375d2165926370501769b1c3b570741db4f091 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Thu, 10 Dec 2020 18:42:44 +0100
Subject: [PATCH] media: i2c: Add driver for OV5693 sensor
Copied from https://github.com/djrscally/surface-ipu3-cameras/tree/18cdb0c0238bc26258501d2bfe95ed7b971803fa
Patchset: cameras
---
drivers/media/i2c/Kconfig | 13 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ad5823.h | 63 ++
drivers/media/i2c/ov5693.c | 1610 ++++++++++++++++++++++++++++++++++++
drivers/media/i2c/ov5693.h | 1415 +++++++++++++++++++++++++++++++
5 files changed, 3102 insertions(+)
create mode 100644 drivers/media/i2c/ad5823.h
create mode 100644 drivers/media/i2c/ov5693.c
create mode 100644 drivers/media/i2c/ov5693.h
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 878f66ef2719..cc3263a91175 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -958,6 +958,19 @@ config VIDEO_OV5675
To compile this driver as a module, choose M here: the
module will be called ov5675.
+config VIDEO_OV5693
+ tristate "OmniVision OV5693 sensor support"
+ depends on I2C && VIDEO_V4L2
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV5693 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5693.
+
config VIDEO_OV5695
tristate "OmniVision OV5695 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index f0a77473979d..2bc1dd3e791b 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
obj-$(CONFIG_VIDEO_OV5675) += ov5675.o
+obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7251) += ov7251.o
diff --git a/drivers/media/i2c/ad5823.h b/drivers/media/i2c/ad5823.h
new file mode 100644
index 000000000000..f1362cd69f6e
--- /dev/null
+++ b/drivers/media/i2c/ad5823.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Support for AD5823 VCM.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#ifndef __AD5823_H__
+#define __AD5823_H__
+
+#include <linux/types.h>
+
+#define AD5823_VCM_ADDR 0x0c
+
+#define AD5823_REG_RESET 0x01
+#define AD5823_REG_MODE 0x02
+#define AD5823_REG_VCM_MOVE_TIME 0x03
+#define AD5823_REG_VCM_CODE_MSB 0x04
+#define AD5823_REG_VCM_CODE_LSB 0x05
+#define AD5823_REG_VCM_THRESHOLD_MSB 0x06
+#define AD5823_REG_VCM_THRESHOLD_LSB 0x07
+
+#define AD5823_REG_LENGTH 0x1
+
+#define AD5823_RING_CTRL_ENABLE 0x04
+#define AD5823_RING_CTRL_DISABLE 0x00
+
+#define AD5823_RESONANCE_PERIOD 100000
+#define AD5823_RESONANCE_COEF 512
+#define AD5823_HIGH_FREQ_RANGE 0x80
+
+#define VCM_CODE_MSB_MASK 0xfc
+#define AD5823_INIT_FOCUS_POS 350
+
+enum ad5823_tok_type {
+ AD5823_8BIT = 0x1,
+ AD5823_16BIT = 0x2,
+};
+
+enum ad5823_vcm_mode {
+ AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */
+ AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */
+ AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */
+ AD5823_ESRC = 0x3, /* Enhanced slew rate control */
+ AD5823_DIRECT = 0x4, /* Direct control */
+};
+
+#define AD5823_INVALID_CONFIG 0xffffffff
+#define AD5823_MAX_FOCUS_POS 1023
+#define DELAY_PER_STEP_NS 1000000
+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
+#endif
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
new file mode 100644
index 000000000000..d6e20909d35e
--- /dev/null
+++ b/drivers/media/i2c/ov5693.c
@@ -0,0 +1,1610 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for OmniVision OV5693 1080p HD camera sensor.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/moduleparam.h>
+#include <media/v4l2-device.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
+
+#include "ov5693.h"
+#include "ad5823.h"
+
+#define __cci_delay(t) \
+ do { \
+ if ((t) < 10) { \
+ usleep_range((t) * 1000, ((t) + 1) * 1000); \
+ } else { \
+ msleep((t)); \
+ } \
+ } while (0)
+
+/* Value 30ms reached through experimentation on byt ecs.
+ * The DS specifies a much lower value but when using a smaller value
+ * the I2C bus sometimes locks up permanently when starting the camera.
+ * This issue could not be reproduced on cht, so we can reduce the
+ * delay value to a lower value when insmod.
+ */
+static uint up_delay = 30;
+module_param(up_delay, uint, 0644);
+MODULE_PARM_DESC(up_delay,
+ "Delay prior to the first CCI transaction for ov5693");
+
+static int vcm_ad_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+
+ msg.addr = VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[0];
+
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err != 1) {
+ dev_err(&client->dev, "%s: vcm i2c fail, err code = %d\n",
+ __func__, err);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ msg.addr = AD5823_VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 0x02;
+ msg.buf = &buf[0];
+
+ if (i2c_transfer(client->adapter, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = 0;
+
+ msg[0].addr = AD5823_VCM_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = 0x01;
+ msg[0].buf = &buf[0];
+
+ msg[1].addr = 0x0c;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 0x01;
+ msg[1].buf = &buf[1];
+ *val = 0;
+ if (i2c_transfer(client->adapter, msg, 2) != 2)
+ return -EIO;
+ *val = buf[1];
+ return 0;
+}
+
+static const u32 ov5693_embedded_effective_size = 28;
+
+/* i2c read/write stuff */
+static int ov5693_read_reg(struct i2c_client *client,
+ u16 data_length, u16 reg, u16 *val)
+{
+ int err;
+ struct i2c_msg msg[2];
+ unsigned char data[6];
+
+ if (!client->adapter) {
+ dev_err(&client->dev, "%s error, no client->adapter\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT &&
+ data_length != OV5693_32BIT) {
+ dev_err(&client->dev, "%s error, invalid data length\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(msg, 0, sizeof(msg));
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = I2C_MSG_LENGTH;
+ msg[0].buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8)(reg >> 8);
+ data[1] = (u8)(reg & 0xff);
+
+ msg[1].addr = client->addr;
+ msg[1].len = data_length;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = data;
+
+ err = i2c_transfer(client->adapter, msg, 2);
+ if (err != 2) {
+ if (err >= 0)
+ err = -EIO;
+ dev_err(&client->dev,
+ "read from offset 0x%x error %d", reg, err);
+ return err;
+ }
+
+ *val = 0;
+ /* high byte comes first */
+ if (data_length == OV5693_8BIT)
+ *val = (u8)data[0];
+ else if (data_length == OV5693_16BIT)
+ *val = be16_to_cpu(*(__be16 *)&data[0]);
+ else
+ *val = be32_to_cpu(*(__be32 *)&data[0]);
+
+ return 0;
+}
+
+static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data)
+{
+ struct i2c_msg msg;
+ const int num_msg = 1;
+ int ret;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret == num_msg ? 0 : -EIO;
+}
+
+static int vcm_dw_i2c_write(struct i2c_client *client, u16 data)
+{
+ struct i2c_msg msg;
+ const int num_msg = 1;
+ int ret;
+ __be16 val;
+
+ val = cpu_to_be16(data);
+ msg.addr = VCM_ADDR;
+ msg.flags = 0;
+ msg.len = OV5693_16BIT;
+ msg.buf = (void *)&val;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret == num_msg ? 0 : -EIO;
+}
+
+/*
+ * Theory: per datasheet, the two VCMs both allow for a 2-byte read.
+ * The DW9714 doesn't actually specify what this does (it has a
+ * two-byte write-only protocol, but specifies the read sequence as
+ * legal), but it returns the same data (zeroes) always, after an
+ * undocumented initial NAK. The AD5823 has a one-byte address
+ * register to which all writes go, and subsequent reads will cycle
+ * through the 8 bytes of registers. Notably, the default values (the
+ * device is always power-cycled affirmatively, so we can rely on
+ * these) in AD5823 are not pairwise repetitions of the same 16 bit
+ * word. So all we have to do is sequentially read two bytes at a
+ * time and see if we detect a difference in any of the first four
+ * pairs.
+ */
+static int vcm_detect(struct i2c_client *client)
+{
+ int i, ret;
+ struct i2c_msg msg;
+ u16 data0 = 0, data;
+
+ for (i = 0; i < 4; i++) {
+ msg.addr = VCM_ADDR;
+ msg.flags = I2C_M_RD;
+ msg.len = sizeof(data);
+ msg.buf = (u8 *)&data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ /*
+ * DW9714 always fails the first read and returns
+ * zeroes for subsequent ones
+ */
+ if (i == 0 && ret == -EREMOTEIO) {
+ data0 = 0;
+ continue;
+ }
+
+ if (i == 0)
+ data0 = data;
+
+ if (data != data0)
+ return VCM_AD5823;
+ }
+ return ret == 1 ? VCM_DW9714 : ret;
+}
+
+static int ov5693_write_reg(struct i2c_client *client, u16 data_length,
+ u16 reg, u16 val)
+{
+ int ret;
+ unsigned char data[4] = {0};
+ __be16 *wreg = (void *)data;
+ const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
+
+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) {
+ dev_err(&client->dev,
+ "%s error, invalid data_length\n", __func__);
+ return -EINVAL;
+ }
+
+ /* high byte goes out first */
+ *wreg = cpu_to_be16(reg);
+
+ if (data_length == OV5693_8BIT) {
+ data[2] = (u8)(val);
+ } else {
+ /* OV5693_16BIT */
+ __be16 *wdata = (void *)&data[2];
+
+ *wdata = cpu_to_be16(val);
+ }
+
+ ret = ov5693_i2c_write(client, len, data);
+ if (ret)
+ dev_err(&client->dev,
+ "write error: wrote 0x%x to offset 0x%x error %d",
+ val, reg, ret);
+
+ return ret;
+}
+
+/*
+ * ov5693_write_reg_array - Initializes a list of OV5693 registers
+ * @client: i2c driver client structure
+ * @reglist: list of registers to be written
+ *
+ * This function initializes a list of registers. When consecutive addresses
+ * are found in a row on the list, this function creates a buffer and sends
+ * consecutive data in a single i2c_transfer().
+ *
+ * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and
+ * __ov5693_write_reg_is_consecutive() are internal functions to
+ * ov5693_write_reg_array_fast() and should be not used anywhere else.
+ *
+ */
+
+static int __ov5693_flush_reg_array(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl)
+{
+ u16 size;
+ __be16 *reg = (void *)&ctrl->buffer.addr;
+
+ if (ctrl->index == 0)
+ return 0;
+
+ size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
+
+ *reg = cpu_to_be16(ctrl->buffer.addr);
+ ctrl->index = 0;
+
+ return ov5693_i2c_write(client, size, (u8 *)reg);
+}
+
+static int __ov5693_buf_reg_array(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl,
+ const struct ov5693_reg *next)
+{
+ int size;
+ __be16 *data16;
+
+ switch (next->type) {
+ case OV5693_8BIT:
+ size = 1;
+ ctrl->buffer.data[ctrl->index] = (u8)next->val;
+ break;
+ case OV5693_16BIT:
+ size = 2;
+
+ data16 = (void *)&ctrl->buffer.data[ctrl->index];
+ *data16 = cpu_to_be16((u16)next->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* When first item is added, we need to store its starting address */
+ if (ctrl->index == 0)
+ ctrl->buffer.addr = next->reg;
+
+ ctrl->index += size;
+
+ /*
+ * Buffer cannot guarantee free space for u32? Better flush it to avoid
+ * possible lack of memory for next item.
+ */
+ if (ctrl->index + sizeof(u16) >= OV5693_MAX_WRITE_BUF_SIZE)
+ return __ov5693_flush_reg_array(client, ctrl);
+
+ return 0;
+}
+
+static int __ov5693_write_reg_is_consecutive(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl,
+ const struct ov5693_reg *next)
+{
+ if (ctrl->index == 0)
+ return 1;
+
+ return ctrl->buffer.addr + ctrl->index == next->reg;
+}
+
+static int ov5693_write_reg_array(struct i2c_client *client,
+ const struct ov5693_reg *reglist)
+{
+ const struct ov5693_reg *next = reglist;
+ struct ov5693_write_ctrl ctrl;
+ int err;
+
+ ctrl.index = 0;
+ for (; next->type != OV5693_TOK_TERM; next++) {
+ switch (next->type & OV5693_TOK_MASK) {
+ case OV5693_TOK_DELAY:
+ err = __ov5693_flush_reg_array(client, &ctrl);
+ if (err)
+ return err;
+ msleep(next->val);
+ break;
+ default:
+ /*
+ * If next address is not consecutive, data needs to be
+ * flushed before proceed.
+ */
+ if (!__ov5693_write_reg_is_consecutive(client, &ctrl,
+ next)) {
+ err = __ov5693_flush_reg_array(client, &ctrl);
+ if (err)
+ return err;
+ }
+ err = __ov5693_buf_reg_array(client, &ctrl, next);
+ if (err) {
+ dev_err(&client->dev,
+ "%s: write error, aborted\n",
+ __func__);
+ return err;
+ }
+ break;
+ }
+ }
+
+ return __ov5693_flush_reg_array(client, &ctrl);
+}
+
+static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg,
+ int gain, int digitgain)
+
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ u16 vts, hts;
+ int ret, exp_val;
+
+ hts = ov5693_res[dev->fmt_idx].pixels_per_line;
+ vts = ov5693_res[dev->fmt_idx].lines_per_frame;
+ /*
+ * If coarse_itg is larger than 1<<15, can not write to reg directly.
+ * The way is to write coarse_itg/2 to the reg, meanwhile write 2*hts
+ * to the reg.
+ */
+ if (coarse_itg > (1 << 15)) {
+ hts = hts * 2;
+ coarse_itg = (int)coarse_itg / 2;
+ }
+ /* group hold */
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_GROUP_ACCESS, 0x00);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_GROUP_ACCESS);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_TIMING_HTS_H, (hts >> 8) & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_TIMING_HTS_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_TIMING_HTS_L, hts & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_TIMING_HTS_L);
+ return ret;
+ }
+ /* Increase the VTS to match exposure + MARGIN */
+ if (coarse_itg > vts - OV5693_INTEGRATION_TIME_MARGIN)
+ vts = (u16)coarse_itg + OV5693_INTEGRATION_TIME_MARGIN;
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_TIMING_VTS_H, (vts >> 8) & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_TIMING_VTS_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_TIMING_VTS_L, vts & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_TIMING_VTS_L);
+ return ret;
+ }
+
+ /* set exposure */
+
+ /* Lower four bit should be 0*/
+ exp_val = coarse_itg << 4;
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_L, exp_val & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_EXPOSURE_L);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_M, (exp_val >> 8) & 0xFF);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_EXPOSURE_M);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_H, (exp_val >> 16) & 0x0F);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_EXPOSURE_H);
+ return ret;
+ }
+
+ /* Analog gain */
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_AGC_L, gain & 0xff);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_AGC_L);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_AGC_H, (gain >> 8) & 0xff);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_AGC_H);
+ return ret;
+ }
+
+ /* Digital gain */
+ if (digitgain) {
+ ret = ov5693_write_reg(client, OV5693_16BIT,
+ OV5693_MWB_RED_GAIN_H, digitgain);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_16BIT,
+ OV5693_MWB_GREEN_GAIN_H, digitgain);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(client, OV5693_16BIT,
+ OV5693_MWB_BLUE_GAIN_H, digitgain);
+ if (ret) {
+ dev_err(&client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+ }
+
+ /* End group */
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_GROUP_ACCESS, 0x10);
+ if (ret)
+ return ret;
+
+ /* Delay launch group */
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_GROUP_ACCESS, 0xa0);
+ if (ret)
+ return ret;
+ return ret;
+}
+
+static int ov5693_set_exposure(struct v4l2_subdev *sd, int exposure,
+ int gain, int digitgain)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ int ret;
+
+ mutex_lock(&dev->input_lock);
+ ret = __ov5693_set_exposure(sd, exposure, gain, digitgain);
+ mutex_unlock(&dev->input_lock);
+
+ return ret;
+}
+
+static int ov5693_read_otp_reg_array(struct i2c_client *client, u16 size,
+ u16 addr, u8 *buf)
+{
+ u16 index;
+ int ret;
+ u16 *p_val = NULL;
+
+ for (index = 0; index <= size; index++) {
+ p_val = (u16 *)(buf + index);
+ ret =
+ ov5693_read_reg(client, OV5693_8BIT, addr + index,
+ p_val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ int ret;
+ int i;
+ u8 *b = buf;
+
+ dev->otp_size = 0;
+ for (i = 1; i < OV5693_OTP_BANK_MAX; i++) {
+ /*set bank NO and OTP read mode. */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_OTP_BANK_REG,
+ (i | 0xc0)); //[7:6] 2'b11 [5:0] bank no
+ if (ret) {
+ dev_err(&client->dev, "failed to prepare OTP page\n");
+ return ret;
+ }
+ //dev_dbg(&client->dev, "write 0x%x->0x%x\n",OV5693_OTP_BANK_REG,(i|0xc0));
+
+ /*enable read */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_OTP_READ_REG,
+ OV5693_OTP_MODE_READ); // enable :1
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set OTP reading mode page");
+ return ret;
+ }
+ //dev_dbg(&client->dev, "write 0x%x->0x%x\n",
+ // OV5693_OTP_READ_REG,OV5693_OTP_MODE_READ);
+
+ /* Reading the OTP data array */
+ ret = ov5693_read_otp_reg_array(client, OV5693_OTP_BANK_SIZE,
+ OV5693_OTP_START_ADDR,
+ b);
+ if (ret) {
+ dev_err(&client->dev, "failed to read OTP data\n");
+ return ret;
+ }
+
+ //dev_dbg(&client->dev,
+ // "BANK[%2d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ // i, *b, *(b + 1), *(b + 2), *(b + 3), *(b + 4), *(b + 5),
+ // *(b + 6), *(b + 7), *(b + 8), *(b + 9), *(b + 10),
+ // *(b + 11), *(b + 12), *(b + 13), *(b + 14), *(b + 15));
+
+ //Intel OTP map, try to read 320byts first.
+ if (i == 21) {
+ if ((*b) == 0) {
+ dev->otp_size = 320;
+ break;
+ }
+ /* (*b) != 0 */
+ b = buf;
+ continue;
+ } else if (i == 24) {
+ // if the first 320bytes data doesn't not exist,
+ // try to read the next 32bytes data.
+ if ((*b) == 0) {
+ dev->otp_size = 32;
+ break;
+ }
+ /* (*b) != 0 */
+ b = buf;
+ continue;
+ } else if (i == 27) {
+ // if the prvious 32bytes data doesn't exist,
+ // try to read the next 32bytes data again.
+ if ((*b) == 0) {
+ dev->otp_size = 32;
+ break;
+ }
+ /* (*b) != 0 */
+ dev->otp_size = 0; // no OTP data.
+ break;
+ }
+
+ b = b + OV5693_OTP_BANK_SIZE;
+ }
+ return 0;
+}
+
+/*
+ * Read otp data and store it into a kmalloced buffer.
+ * The caller must kfree the buffer when no more needed.
+ * @size: set to the size of the returned otp data.
+ */
+static void *ov5693_otp_read(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 *buf;
+ int ret;
+
+ buf = devm_kzalloc(&client->dev, (OV5693_OTP_DATA_SIZE + 16), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ //otp valid after mipi on and sw stream on
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_FRAME_OFF_NUM, 0x00);
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_STREAM, OV5693_START_STREAMING);
+
+ ret = __ov5693_otp_read(sd, buf);
+
+ //mipi off and sw stream off after otp read
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_FRAME_OFF_NUM, 0x0f);
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_STREAM, OV5693_STOP_STREAMING);
+
+ /* Driver has failed to find valid data */
+ if (ret) {
+ dev_err(&client->dev, "sensor found no valid OTP data\n");
+ return ERR_PTR(ret);
+ }
+
+ return buf;
+}
+
+/*
+ * This returns the exposure time being used. This should only be used
+ * for filling in EXIF data, not for actual image processing.
+ */
+static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 reg_v, reg_v2;
+ int ret;
+
+ /* get exposure */
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_L,
+ &reg_v);
+ if (ret)
+ goto err;
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_M,
+ &reg_v2);
+ if (ret)
+ goto err;
+
+ reg_v += reg_v2 << 8;
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_H,
+ &reg_v2);
+ if (ret)
+ goto err;
+
+ *value = reg_v + (((u32)reg_v2 << 16));
+err:
+ return ret;
+}
+
+static int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = -EINVAL;
+ u8 vcm_code;
+
+ ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code);
+ if (ret)
+ return ret;
+
+ /* set reg VCM_CODE_MSB Bit[1:0] */
+ vcm_code = (vcm_code & VCM_CODE_MSB_MASK) |
+ ((val >> 8) & ~VCM_CODE_MSB_MASK);
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code);
+ if (ret)
+ return ret;
+
+ /* set reg VCM_CODE_LSB Bit[7:0] */
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, (val & 0xff));
+ if (ret)
+ return ret;
+
+ /* set required vcm move time */
+ vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF
+ - AD5823_HIGH_FREQ_RANGE;
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code);
+
+ return ret;
+}
+
+static int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+{
+ value = min(value, AD5823_MAX_FOCUS_POS);
+ return ad5823_t_focus_vcm(sd, value);
+}
+
+static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ dev_dbg(&client->dev, "%s: FOCUS_POS: 0x%x\n", __func__, value);
+ value = clamp(value, 0, OV5693_VCM_MAX_FOCUS_POS);
+ if (dev->vcm == VCM_DW9714) {
+ if (dev->vcm_update) {
+ ret = vcm_dw_i2c_write(client, VCM_PROTECTION_OFF);
+ if (ret)
+ return ret;
+ ret = vcm_dw_i2c_write(client, DIRECT_VCM);
+ if (ret)
+ return ret;
+ ret = vcm_dw_i2c_write(client, VCM_PROTECTION_ON);
+ if (ret)
+ return ret;
+ dev->vcm_update = false;
+ }
+ ret = vcm_dw_i2c_write(client,
+ vcm_val(value, VCM_DEFAULT_S));
+ } else if (dev->vcm == VCM_AD5823) {
+ ad5823_t_focus_abs(sd, value);
+ }
+ if (ret == 0) {
+ dev->number_of_steps = value - dev->focus;
+ dev->focus = value;
+ dev->timestamp_t_focus_abs = ktime_get();
+ } else {
+ dev_err(&client->dev, "%s: i2c failed. ret %d\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ return ov5693_t_focus_abs(sd, dev->focus + value);
+}
+
+#define DELAY_PER_STEP_NS 1000000
+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
+
+static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *dev =
+ container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_t_focus_abs(&dev->sd, ctrl->val);
+ break;
+ case V4L2_CID_FOCUS_RELATIVE:
+ dev_dbg(&client->dev, "%s: CID_FOCUS_RELATIVE:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_t_focus_rel(&dev->sd, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int ov5693_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *dev =
+ container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_ABSOLUTE:
+ ret = ov5693_q_exposure(&dev->sd, &ctrl->val);
+ break;
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ /* NOTE: there was atomisp-specific function ov5693_q_focus_abs() */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ctrl_ops = {
+ .s_ctrl = ov5693_s_ctrl,
+ .g_volatile_ctrl = ov5693_g_volatile_ctrl
+};
+
+static const struct v4l2_ctrl_config ov5693_controls[] = {
+ {
+ .ops = &ctrl_ops,
+ .id = V4L2_CID_EXPOSURE_ABSOLUTE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .min = 0x0,
+ .max = 0xffff,
+ .step = 0x01,
+ .def = 0x00,
+ .flags = 0,
+ },
+ {
+ .ops = &ctrl_ops,
+ .id = V4L2_CID_FOCUS_ABSOLUTE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "focus move absolute",
+ .min = 0,
+ .max = OV5693_VCM_MAX_FOCUS_POS,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ },
+ {
+ .ops = &ctrl_ops,
+ .id = V4L2_CID_FOCUS_RELATIVE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "focus move relative",
+ .min = OV5693_VCM_MAX_FOCUS_NEG,
+ .max = OV5693_VCM_MAX_FOCUS_POS,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ },
+};
+
+static int ov5693_init(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (!dev->has_vcm)
+ return 0;
+
+ dev_info(&client->dev, "%s\n", __func__);
+ mutex_lock(&dev->input_lock);
+ dev->vcm_update = false;
+
+ if (dev->vcm == VCM_AD5823) {
+ ret = vcm_ad_i2c_wr8(client, 0x01, 0x01); /* vcm init test */
+ if (ret)
+ dev_err(&client->dev,
+ "vcm reset failed\n");
+ /*change the mode*/
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB,
+ AD5823_RING_CTRL_ENABLE);
+ if (ret)
+ dev_err(&client->dev,
+ "vcm enable ringing failed\n");
+ ret = ad5823_i2c_write(client, AD5823_REG_MODE,
+ AD5823_ARC_RES1);
+ if (ret)
+ dev_err(&client->dev,
+ "vcm change mode failed\n");
+ }
+
+ /*change initial focus value for ad5823*/
+ if (dev->vcm == VCM_AD5823) {
+ dev->focus = AD5823_INIT_FOCUS_POS;
+ ov5693_t_focus_abs(sd, AD5823_INIT_FOCUS_POS);
+ } else {
+ dev->focus = 0;
+ ov5693_t_focus_abs(sd, 0);
+ }
+
+ mutex_unlock(&dev->input_lock);
+
+ return 0;
+}
+
+static int __power_up(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *sensor = to_ov5693_sensor(sd);
+ int ret;
+
+ if (sensor->indicator_led)
+ gpiod_set_value_cansleep(sensor->indicator_led, 1);
+
+ ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES, sensor->supplies);
+ if (ret)
+ goto fail_power;
+
+ __cci_delay(up_delay);
+
+ return 0;
+
+fail_power:
+ if (sensor->indicator_led)
+ gpiod_set_value_cansleep(sensor->indicator_led, 0);
+ dev_err(&client->dev, "sensor power-up failed\n");
+
+ return ret;
+}
+
+static int power_down(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ dev->focus = OV5693_INVALID_CONFIG;
+
+ if (dev->indicator_led)
+ gpiod_set_value_cansleep(dev->indicator_led, 0);
+
+ return regulator_bulk_disable(OV5693_NUM_SUPPLIES, dev->supplies);
+}
+
+static int power_up(struct v4l2_subdev *sd)
+{
+ static const int retry_count = 4;
+ int i, ret;
+
+ for (i = 0; i < retry_count; i++) {
+ ret = __power_up(sd);
+ if (!ret)
+ return 0;
+
+ power_down(sd);
+ }
+ return ret;
+}
+
+static int ov5693_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ dev_info(&client->dev, "%s: on %d\n", __func__, on);
+
+ if (on == 0)
+ return power_down(sd);
+
+ /* on == 1 */
+ ret = power_up(sd);
+ if (!ret) {
+ ret = ov5693_init(sd);
+ /* restore settings */
+ ov5693_res = ov5693_res_preview;
+ N_RES = N_RES_PREVIEW;
+ }
+
+ return ret;
+}
+
+/*
+ * distance - calculate the distance
+ * @res: resolution
+ * @w: width
+ * @h: height
+ *
+ * Get the gap between res_w/res_h and w/h.
+ * distance = (res_w/res_h - w/h) / (w/h) * 8192
+ * res->width/height smaller than w/h wouldn't be considered.
+ * The gap of ratio larger than 1/8 wouldn't be considered.
+ * Returns the value of gap or -1 if fail.
+ */
+#define LARGEST_ALLOWED_RATIO_MISMATCH 1024
+static int distance(struct ov5693_resolution *res, u32 w, u32 h)
+{
+ int ratio;
+ int distance;
+
+ if (w == 0 || h == 0 ||
+ res->width < w || res->height < h)
+ return -1;
+
+ ratio = res->width << 13;
+ ratio /= w;
+ ratio *= h;
+ ratio /= res->height;
+
+ distance = abs(ratio - 8192);
+
+ if (distance > LARGEST_ALLOWED_RATIO_MISMATCH)
+ return -1;
+
+ return distance;
+}
+
+/* Return the nearest higher resolution index
+ * Firstly try to find the approximate aspect ratio resolution
+ * If we find multiple same AR resolutions, choose the
+ * minimal size.
+ */
+static int nearest_resolution_index(int w, int h)
+{
+ int i;
+ int idx = -1;
+ int dist;
+ int min_dist = INT_MAX;
+ int min_res_w = INT_MAX;
+ struct ov5693_resolution *tmp_res = NULL;
+
+ for (i = 0; i < N_RES; i++) {
+ tmp_res = &ov5693_res[i];
+ dist = distance(tmp_res, w, h);
+ if (dist == -1)
+ continue;
+ if (dist < min_dist) {
+ min_dist = dist;
+ idx = i;
+ min_res_w = ov5693_res[i].width;
+ continue;
+ }
+ if (dist == min_dist && ov5693_res[i].width < min_res_w)
+ idx = i;
+ }
+
+ return idx;
+}
+
+static int get_resolution_index(int w, int h)
+{
+ int i;
+
+ for (i = 0; i < N_RES; i++) {
+ if (w != ov5693_res[i].width)
+ continue;
+ if (h != ov5693_res[i].height)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+/* TODO: remove it. */
+static int startup(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_RESET, 0x01);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 reset err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_global_setting);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_res[dev->fmt_idx].regs);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ov5693_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+ int idx;
+ int cnt;
+
+ if (format->pad)
+ return -EINVAL;
+ if (!fmt)
+ return -EINVAL;
+
+ mutex_lock(&dev->input_lock);
+ idx = nearest_resolution_index(fmt->width, fmt->height);
+ if (idx == -1) {
+ /* return the largest resolution */
+ fmt->width = ov5693_res[N_RES - 1].width;
+ fmt->height = ov5693_res[N_RES - 1].height;
+ } else {
+ fmt->width = ov5693_res[idx].width;
+ fmt->height = ov5693_res[idx].height;
+ }
+
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ cfg->try_fmt = *fmt;
+ ret = 0;
+ goto mutex_unlock;
+ }
+
+ dev->fmt_idx = get_resolution_index(fmt->width, fmt->height);
+ if (dev->fmt_idx == -1) {
+ dev_err(&client->dev, "get resolution fail\n");
+ ret = -EINVAL;
+ goto mutex_unlock;
+ }
+
+ for (cnt = 0; cnt < OV5693_POWER_UP_RETRY_NUM; cnt++) {
+// power_down(sd);
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "power up failed\n");
+ continue;
+ }
+
+ mutex_unlock(&dev->input_lock);
+ ov5693_init(sd);
+ mutex_lock(&dev->input_lock);
+ ret = startup(sd);
+ if (ret)
+ dev_err(&client->dev, " startup() FAILED!\n");
+ else
+ break;
+ }
+ if (cnt == OV5693_POWER_UP_RETRY_NUM) {
+ dev_err(&client->dev, "power up failed, gave up\n");
+ goto mutex_unlock;
+ }
+
+ /*
+ * After sensor settings are set to HW, sometimes stream is started.
+ * This would cause ISP timeout because ISP is not ready to receive
+ * data yet. So add stop streaming here.
+ */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
+ OV5693_STOP_STREAMING);
+ if (ret)
+ dev_warn(&client->dev, "ov5693 stream off err\n");
+
+mutex_unlock:
+ mutex_unlock(&dev->input_lock);
+ return ret;
+}
+
+static int ov5693_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (!fmt)
+ return -EINVAL;
+
+ fmt->width = ov5693_res[dev->fmt_idx].width;
+ fmt->height = ov5693_res[dev->fmt_idx].height;
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov5693_detect(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u16 high, low;
+ int ret;
+ u16 id;
+ u8 revision;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_CHIP_ID_H, &high);
+ if (ret) {
+ dev_err(&client->dev, "sensor_id_high = 0x%x\n", high);
+ return -ENODEV;
+ }
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_CHIP_ID_L, &low);
+ id = ((((u16)high) << 8) | (u16)low);
+
+ if (id != OV5693_ID) {
+ dev_err(&client->dev, "sensor ID error 0x%x\n", id);
+ return -ENODEV;
+ }
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_SUB_ID, &high);
+ revision = (u8)high & 0x0f;
+
+ dev_info(&client->dev, "sensor_revision = 0x%x\n", revision);
+ dev_info(&client->dev, "sensor_address = 0x%02x\n", client->addr);
+ dev_info(&client->dev, "detect ov5693 success\n");
+ return 0;
+}
+
+static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ mutex_lock(&dev->input_lock);
+
+ /* power_on() here before streaming for regular PCs. */
+ if (enable) {
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "sensor power-up error\n");
+// power_down(sd);
+ goto out;
+ }
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
+ enable ? OV5693_START_STREAMING :
+ OV5693_STOP_STREAMING);
+
+ /* power_off() here after streaming for regular PCs. */
+ if (!enable)
+ power_down(sd);
+
+out:
+ mutex_unlock(&dev->input_lock);
+
+ return ret;
+}
+
+static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&dev->input_lock);
+ /* power off the module, then power on it in future
+ * as first power on by board may not fulfill the
+ * power on sequqence needed by the module
+ */
+// ret = power_down(sd);
+// if (ret) {
+// dev_err(&client->dev, "ov5693 power-off err.\n");
+// goto fail_power_off;
+// }
+
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 power-up err.\n");
+ goto fail_power_on;
+ }
+
+ if (!dev->vcm)
+ dev->vcm = vcm_detect(client);
+
+ /* config & detect sensor */
+ ret = ov5693_detect(client);
+ if (ret) {
+ dev_err(&client->dev, "ov5693_detect err s_config.\n");
+ goto fail_power_on;
+ }
+
+ dev->otp_data = ov5693_otp_read(sd);
+
+ /* turn off sensor, after probed */
+ ret = power_down(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 power-off err.\n");
+ goto fail_power_on;
+ }
+ mutex_unlock(&dev->input_lock);
+
+ return ret;
+
+fail_power_on:
+ power_down(sd);
+ dev_err(&client->dev, "sensor power-gating failed\n");
+fail_power_off:
+ mutex_unlock(&dev->input_lock);
+ return ret;
+}
+
+static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ interval->interval.numerator = 1;
+ interval->interval.denominator = ov5693_res[dev->fmt_idx].fps;
+
+ return 0;
+}
+
+static int ov5693_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= MAX_FMTS)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int ov5693_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int index = fse->index;
+
+ if (index >= N_RES)
+ return -EINVAL;
+
+ fse->min_width = ov5693_res[index].width;
+ fse->min_height = ov5693_res[index].height;
+ fse->max_width = ov5693_res[index].width;
+ fse->max_height = ov5693_res[index].height;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov5693_video_ops = {
+ .s_stream = ov5693_s_stream,
+ .g_frame_interval = ov5693_g_frame_interval,
+};
+
+static const struct v4l2_subdev_core_ops ov5693_core_ops = {
+ .s_power = ov5693_s_power,
+};
+
+static const struct v4l2_subdev_pad_ops ov5693_pad_ops = {
+ .enum_mbus_code = ov5693_enum_mbus_code,
+ .enum_frame_size = ov5693_enum_frame_size,
+ .get_fmt = ov5693_get_fmt,
+ .set_fmt = ov5693_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov5693_ops = {
+ .core = &ov5693_core_ops,
+ .video = &ov5693_video_ops,
+ .pad = &ov5693_pad_ops,
+};
+
+static int ov5693_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ unsigned int i = OV5693_NUM_SUPPLIES;
+
+ dev_info(&client->dev, "%s...\n", __func__);
+
+ gpiod_put(ov5693->reset);
+ gpiod_put(ov5693->indicator_led);
+
+ while (i--)
+ regulator_put(ov5693->supplies[i].consumer);
+
+ v4l2_async_unregister_subdev(sd);
+
+ media_entity_cleanup(&ov5693->sd.entity);
+ v4l2_ctrl_handler_free(&ov5693->ctrl_handler);
+ kfree(ov5693);
+
+ return 0;
+}
+
+static int ov5693_init_controls(struct ov5693_device *ov5693)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
+ struct v4l2_ctrl *ctrl;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler,
+ ARRAY_SIZE(ov5693_controls));
+ if (ret) {
+ ov5693_remove(client);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ov5693_controls); i++)
+ v4l2_ctrl_new_custom(&ov5693->ctrl_handler,
+ &ov5693_controls[i],
+ NULL);
+
+ /* link freq */
+ ctrl = v4l2_ctrl_new_int_menu(&ov5693->ctrl_handler, NULL,
+ V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* pixel rate */
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, OV5693_PIXEL_RATE, 1, OV5693_PIXEL_RATE);
+
+ if (ov5693->ctrl_handler.error) {
+ ov5693_remove(client);
+ return ov5693->ctrl_handler.error;
+ }
+
+ /* Use same lock for controls as for everything else. */
+ ov5693->ctrl_handler.lock = &ov5693->input_lock;
+ ov5693->sd.ctrl_handler = &ov5693->ctrl_handler;
+
+ return 0;
+}
+
+static int ov5693_configure_gpios(struct ov5693_device *ov5693)
+{
+ ov5693->reset = gpiod_get_index(&ov5693->client->dev, "reset", 0,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->reset)) {
+ dev_err(&ov5693->client->dev, "Couldn't find reset GPIO\n");
+ return -EINVAL;
+ }
+
+ ov5693->indicator_led = gpiod_get_index_optional(&ov5693->client->dev,
+ "indicator-led", 0, GPIOD_OUT_HIGH);
+
+ if (IS_ERR(ov5693->indicator_led)) {
+ dev_err(&ov5693->client->dev, "Couldn't find indicator-led GPIO\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ov5693_get_regulators(struct ov5693_device *ov5693)
+{
+ unsigned int i;
+
+ for (i = 0; i < OV5693_NUM_SUPPLIES; i++)
+ ov5693->supplies[i].supply = ov5693_supply_names[i];
+
+ return regulator_bulk_get(&ov5693->client->dev,
+ OV5693_NUM_SUPPLIES,
+ ov5693->supplies);
+}
+
+static int ov5693_probe(struct i2c_client *client)
+{
+ struct ov5693_device *ov5693;
+ int ret = 0;
+
+ dev_info(&client->dev, "%s() called", __func__);
+
+ ov5693 = kzalloc(sizeof(*ov5693), GFP_KERNEL);
+ if (!ov5693)
+ return -ENOMEM;
+
+ ov5693->client = client;
+
+ /* check if VCM device exists */
+ /* TODO: read from SSDB */
+ ov5693->has_vcm = false;
+
+ mutex_init(&ov5693->input_lock);
+
+ v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops);
+
+ ret = ov5693_configure_gpios(ov5693);
+ if (ret)
+ goto out_free;
+
+ ret = ov5693_get_regulators(ov5693);
+ if (ret)
+ goto out_put_reset;
+
+ ret = ov5693_s_config(&ov5693->sd, client->irq);
+ if (ret)
+ goto out_put_reset;
+
+ ov5693->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov5693->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov5693->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ ov5693->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = ov5693_init_controls(ov5693);
+ if (ret)
+ ov5693_remove(client);
+
+ ret = media_entity_pads_init(&ov5693->sd.entity, 1, &ov5693->pad);
+ if (ret)
+ ov5693_remove(client);
+
+ ret = v4l2_async_register_subdev_sensor_common(&ov5693->sd);
+ if (ret) {
+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", ret);
+ goto media_entity_cleanup;
+ }
+
+ return ret;
+
+media_entity_cleanup:
+ media_entity_cleanup(&ov5693->sd.entity);
+out_put_reset:
+ gpiod_put(ov5693->reset);
+out_free:
+ v4l2_device_unregister_subdev(&ov5693->sd);
+ kfree(ov5693);
+ return ret;
+}
+
+static const struct acpi_device_id ov5693_acpi_match[] = {
+ {"INT33BE"},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match);
+
+static struct i2c_driver ov5693_driver = {
+ .driver = {
+ .name = "ov5693",
+ .acpi_match_table = ov5693_acpi_match,
+ },
+ .probe_new = ov5693_probe,
+ .remove = ov5693_remove,
+};
+module_i2c_driver(ov5693_driver);
+
+MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
new file mode 100644
index 000000000000..839b0773e2bc
--- /dev/null
+++ b/drivers/media/i2c/ov5693.h
@@ -0,0 +1,1415 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Support for OmniVision OV5693 5M camera sensor.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#ifndef __OV5693_H__
+#define __OV5693_H__
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+
+#define OV5693_HID "INT33BE"
+
+/*
+ * FIXME: non-preview resolutions are currently broken
+ */
+#define ENABLE_NON_PREVIEW 0
+
+#define OV5693_POWER_UP_RETRY_NUM 5
+
+/* Defines for register writes and register array processing */
+#define I2C_MSG_LENGTH 0x2
+#define I2C_RETRY_COUNT 5
+
+#define OV5693_FOCAL_LENGTH_NUM 334 /*3.34mm*/
+#define OV5693_FOCAL_LENGTH_DEM 100
+#define OV5693_F_NUMBER_DEFAULT_NUM 24
+#define OV5693_F_NUMBER_DEM 10
+
+#define MAX_FMTS 1
+
+/* sensor_mode_data read_mode adaptation */
+#define OV5693_READ_MODE_BINNING_ON 0x0400
+#define OV5693_READ_MODE_BINNING_OFF 0x00
+#define OV5693_INTEGRATION_TIME_MARGIN 8
+
+#define OV5693_MAX_EXPOSURE_VALUE 0xFFF1
+#define OV5693_MAX_GAIN_VALUE 0xFF
+
+/*
+ * focal length bits definition:
+ * bits 31-16: numerator, bits 15-0: denominator
+ */
+#define OV5693_FOCAL_LENGTH_DEFAULT 0x1B70064
+
+/*
+ * current f-number bits definition:
+ * bits 31-16: numerator, bits 15-0: denominator
+ */
+#define OV5693_F_NUMBER_DEFAULT 0x18000a
+
+/*
+ * f-number range bits definition:
+ * bits 31-24: max f-number numerator
+ * bits 23-16: max f-number denominator
+ * bits 15-8: min f-number numerator
+ * bits 7-0: min f-number denominator
+ */
+#define OV5693_F_NUMBER_RANGE 0x180a180a
+#define OV5693_ID 0x5690
+
+#define OV5693_FINE_INTG_TIME_MIN 0
+#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0
+#define OV5693_COARSE_INTG_TIME_MIN 1
+#define OV5693_COARSE_INTG_TIME_MAX_MARGIN 6
+
+#define OV5693_BIN_FACTOR_MAX 4
+/*
+ * OV5693 System control registers
+ */
+#define OV5693_SW_SLEEP 0x0100
+#define OV5693_SW_RESET 0x0103
+#define OV5693_SW_STREAM 0x0100
+
+#define OV5693_SC_CMMN_CHIP_ID_H 0x300A
+#define OV5693_SC_CMMN_CHIP_ID_L 0x300B
+#define OV5693_SC_CMMN_SCCB_ID 0x300C
+#define OV5693_SC_CMMN_SUB_ID 0x302A /* process, version*/
+/*Bit[7:4] Group control, Bit[3:0] Group ID*/
+#define OV5693_GROUP_ACCESS 0x3208
+/*
+ * Bit[3:0] Bit[19:16] of exposure,
+ * remaining 16 bits lies in Reg0x3501&Reg0x3502
+ */
+#define OV5693_EXPOSURE_H 0x3500
+#define OV5693_EXPOSURE_M 0x3501
+#define OV5693_EXPOSURE_L 0x3502
+/*Bit[1:0] means Bit[9:8] of gain*/
+#define OV5693_AGC_H 0x350A
+#define OV5693_AGC_L 0x350B /*Bit[7:0] of gain*/
+
+#define OV5693_HORIZONTAL_START_H 0x3800 /*Bit[11:8]*/
+#define OV5693_HORIZONTAL_START_L 0x3801 /*Bit[7:0]*/
+#define OV5693_VERTICAL_START_H 0x3802 /*Bit[11:8]*/
+#define OV5693_VERTICAL_START_L 0x3803 /*Bit[7:0]*/
+#define OV5693_HORIZONTAL_END_H 0x3804 /*Bit[11:8]*/
+#define OV5693_HORIZONTAL_END_L 0x3805 /*Bit[7:0]*/
+#define OV5693_VERTICAL_END_H 0x3806 /*Bit[11:8]*/
+#define OV5693_VERTICAL_END_L 0x3807 /*Bit[7:0]*/
+#define OV5693_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /*Bit[3:0]*/
+#define OV5693_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /*Bit[7:0]*/
+#define OV5693_VERTICAL_OUTPUT_SIZE_H 0x380a /*Bit[3:0]*/
+#define OV5693_VERTICAL_OUTPUT_SIZE_L 0x380b /*Bit[7:0]*/
+/*High 8-bit, and low 8-bit HTS address is 0x380d*/
+#define OV5693_TIMING_HTS_H 0x380C
+/*High 8-bit, and low 8-bit HTS address is 0x380d*/
+#define OV5693_TIMING_HTS_L 0x380D
+/*High 8-bit, and low 8-bit HTS address is 0x380f*/
+#define OV5693_TIMING_VTS_H 0x380e
+/*High 8-bit, and low 8-bit HTS address is 0x380f*/
+#define OV5693_TIMING_VTS_L 0x380f
+
+#define OV5693_MWB_RED_GAIN_H 0x3400
+#define OV5693_MWB_GREEN_GAIN_H 0x3402
+#define OV5693_MWB_BLUE_GAIN_H 0x3404
+#define OV5693_MWB_GAIN_MAX 0x0fff
+
+#define OV5693_START_STREAMING 0x01
+#define OV5693_STOP_STREAMING 0x00
+
+#define VCM_ADDR 0x0c
+#define VCM_CODE_MSB 0x04
+
+#define OV5693_INVALID_CONFIG 0xffffffff
+
+#define OV5693_VCM_SLEW_STEP 0x30F0
+#define OV5693_VCM_SLEW_STEP_MAX 0x7
+#define OV5693_VCM_SLEW_STEP_MASK 0x7
+#define OV5693_VCM_CODE 0x30F2
+#define OV5693_VCM_SLEW_TIME 0x30F4
+#define OV5693_VCM_SLEW_TIME_MAX 0xffff
+#define OV5693_VCM_ENABLE 0x8000
+
+#define OV5693_VCM_MAX_FOCUS_NEG -1023
+#define OV5693_VCM_MAX_FOCUS_POS 1023
+
+#define DLC_ENABLE 1
+#define DLC_DISABLE 0
+#define VCM_PROTECTION_OFF 0xeca3
+#define VCM_PROTECTION_ON 0xdc51
+#define VCM_DEFAULT_S 0x0
+#define vcm_step_s(a) ((u8)((a) & 0xf))
+#define vcm_step_mclk(a) ((u8)(((a) >> 4) & 0x3))
+#define vcm_dlc_mclk(dlc, mclk) ((u16)(((dlc) << 3) | (mclk) | 0xa104))
+#define vcm_tsrc(tsrc) ((u16)((tsrc) << 3 | 0xf200))
+#define vcm_val(data, s) ((u16)((data) << 4 | (s)))
+#define DIRECT_VCM vcm_dlc_mclk(0, 0)
+
+/* Defines for OTP Data Registers */
+#define OV5693_FRAME_OFF_NUM 0x4202
+#define OV5693_OTP_BYTE_MAX 32 //change to 32 as needed by otpdata
+#define OV5693_OTP_SHORT_MAX 16
+#define OV5693_OTP_START_ADDR 0x3D00
+#define OV5693_OTP_END_ADDR 0x3D0F
+#define OV5693_OTP_DATA_SIZE 320
+#define OV5693_OTP_PROGRAM_REG 0x3D80
+#define OV5693_OTP_READ_REG 0x3D81 // 1:Enable 0:disable
+#define OV5693_OTP_BANK_REG 0x3D84 //otp bank and mode
+#define OV5693_OTP_READY_REG_DONE 1
+#define OV5693_OTP_BANK_MAX 28
+#define OV5693_OTP_BANK_SIZE 16 //16 bytes per bank
+#define OV5693_OTP_READ_ONETIME 16
+#define OV5693_OTP_MODE_READ 1
+
+/* link freq and pixel rate required for IPU3 */
+#define OV5693_LINK_FREQ_640MHZ 640000000
+/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample
+ * To avoid integer overflow, dividing by bits_per_sample first.
+ */
+#define OV5693_PIXEL_RATE ((OV5693_LINK_FREQ_640MHZ / 10) * 2 * 2)
+static const s64 link_freq_menu_items[] = {
+ OV5693_LINK_FREQ_640MHZ
+};
+
+#define OV5693_NUM_SUPPLIES 2
+static const char *const ov5693_supply_names[] = {
+ "avdd",
+ "dovdd",
+};
+
+struct regval_list {
+ u16 reg_num;
+ u8 value;
+};
+
+struct ov5693_resolution {
+ u8 *desc;
+ const struct ov5693_reg *regs;
+ int res;
+ int width;
+ int height;
+ int fps;
+ int pix_clk_freq;
+ u16 pixels_per_line;
+ u16 lines_per_frame;
+ u8 bin_factor_x;
+ u8 bin_factor_y;
+ u8 bin_mode;
+ bool used;
+};
+
+struct ov5693_format {
+ u8 *desc;
+ u32 pixelformat;
+ struct ov5693_reg *regs;
+};
+
+enum vcm_type {
+ VCM_UNKNOWN,
+ VCM_AD5823,
+ VCM_DW9714,
+};
+
+/*
+ * ov5693 device structure.
+ */
+struct ov5693_device {
+ struct i2c_client *client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt format;
+ struct mutex input_lock;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct gpio_desc *reset;
+ struct gpio_desc *indicator_led;
+ struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
+
+ struct camera_sensor_platform_data *platform_data;
+ ktime_t timestamp_t_focus_abs;
+ int vt_pix_clk_freq_mhz;
+ int fmt_idx;
+ int run_mode;
+ int otp_size;
+ u8 *otp_data;
+ u32 focus;
+ s16 number_of_steps;
+ u8 res;
+ u8 type;
+ bool vcm_update;
+ enum vcm_type vcm;
+
+ bool has_vcm;
+};
+
+enum ov5693_tok_type {
+ OV5693_8BIT = 0x0001,
+ OV5693_16BIT = 0x0002,
+ OV5693_32BIT = 0x0004,
+ OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */
+ OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */
+ OV5693_TOK_MASK = 0xfff0
+};
+
+/**
+ * struct ov5693_reg - MI sensor register format
+ * @type: type of the register
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ *
+ * Define a structure for sensor register initialization values
+ */
+struct ov5693_reg {
+ enum ov5693_tok_type type;
+ u16 reg;
+ u32 val; /* @set value for read/mod/write, @mask */
+};
+
+#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd)
+
+#define OV5693_MAX_WRITE_BUF_SIZE 30
+
+struct ov5693_write_buffer {
+ u16 addr;
+ u8 data[OV5693_MAX_WRITE_BUF_SIZE];
+};
+
+struct ov5693_write_ctrl {
+ int index;
+ struct ov5693_write_buffer buffer;
+};
+
+static struct ov5693_reg const ov5693_global_setting[] = {
+ {OV5693_8BIT, 0x0103, 0x01},
+ {OV5693_8BIT, 0x3001, 0x0a},
+ {OV5693_8BIT, 0x3002, 0x80},
+ {OV5693_8BIT, 0x3006, 0x00},
+ {OV5693_8BIT, 0x3011, 0x21},
+ {OV5693_8BIT, 0x3012, 0x09},
+ {OV5693_8BIT, 0x3013, 0x10},
+ {OV5693_8BIT, 0x3014, 0x00},
+ {OV5693_8BIT, 0x3015, 0x08},
+ {OV5693_8BIT, 0x3016, 0xf0},
+ {OV5693_8BIT, 0x3017, 0xf0},
+ {OV5693_8BIT, 0x3018, 0xf0},
+ {OV5693_8BIT, 0x301b, 0xb4},
+ {OV5693_8BIT, 0x301d, 0x02},
+ {OV5693_8BIT, 0x3021, 0x00},
+ {OV5693_8BIT, 0x3022, 0x01},
+ {OV5693_8BIT, 0x3028, 0x44},
+ {OV5693_8BIT, 0x3098, 0x02},
+ {OV5693_8BIT, 0x3099, 0x19},
+ {OV5693_8BIT, 0x309a, 0x02},
+ {OV5693_8BIT, 0x309b, 0x01},
+ {OV5693_8BIT, 0x309c, 0x00},
+ {OV5693_8BIT, 0x30a0, 0xd2},
+ {OV5693_8BIT, 0x30a2, 0x01},
+ {OV5693_8BIT, 0x30b2, 0x00},
+ {OV5693_8BIT, 0x30b3, 0x7d},
+ {OV5693_8BIT, 0x30b4, 0x03},
+ {OV5693_8BIT, 0x30b5, 0x04},
+ {OV5693_8BIT, 0x30b6, 0x01},
+ {OV5693_8BIT, 0x3104, 0x21},
+ {OV5693_8BIT, 0x3106, 0x00},
+ {OV5693_8BIT, 0x3400, 0x04},
+ {OV5693_8BIT, 0x3401, 0x00},
+ {OV5693_8BIT, 0x3402, 0x04},
+ {OV5693_8BIT, 0x3403, 0x00},
+ {OV5693_8BIT, 0x3404, 0x04},
+ {OV5693_8BIT, 0x3405, 0x00},
+ {OV5693_8BIT, 0x3406, 0x01},
+ {OV5693_8BIT, 0x3500, 0x00},
+ {OV5693_8BIT, 0x3503, 0x07},
+ {OV5693_8BIT, 0x3504, 0x00},
+ {OV5693_8BIT, 0x3505, 0x00},
+ {OV5693_8BIT, 0x3506, 0x00},
+ {OV5693_8BIT, 0x3507, 0x02},
+ {OV5693_8BIT, 0x3508, 0x00},
+ {OV5693_8BIT, 0x3509, 0x10},
+ {OV5693_8BIT, 0x350a, 0x00},
+ {OV5693_8BIT, 0x350b, 0x40},
+ {OV5693_8BIT, 0x3601, 0x0a},
+ {OV5693_8BIT, 0x3602, 0x38},
+ {OV5693_8BIT, 0x3612, 0x80},
+ {OV5693_8BIT, 0x3620, 0x54},
+ {OV5693_8BIT, 0x3621, 0xc7},
+ {OV5693_8BIT, 0x3622, 0x0f},
+ {OV5693_8BIT, 0x3625, 0x10},
+ {OV5693_8BIT, 0x3630, 0x55},
+ {OV5693_8BIT, 0x3631, 0xf4},
+ {OV5693_8BIT, 0x3632, 0x00},
+ {OV5693_8BIT, 0x3633, 0x34},
+ {OV5693_8BIT, 0x3634, 0x02},
+ {OV5693_8BIT, 0x364d, 0x0d},
+ {OV5693_8BIT, 0x364f, 0xdd},
+ {OV5693_8BIT, 0x3660, 0x04},
+ {OV5693_8BIT, 0x3662, 0x10},
+ {OV5693_8BIT, 0x3663, 0xf1},
+ {OV5693_8BIT, 0x3665, 0x00},
+ {OV5693_8BIT, 0x3666, 0x20},
+ {OV5693_8BIT, 0x3667, 0x00},
+ {OV5693_8BIT, 0x366a, 0x80},
+ {OV5693_8BIT, 0x3680, 0xe0},
+ {OV5693_8BIT, 0x3681, 0x00},
+ {OV5693_8BIT, 0x3700, 0x42},
+ {OV5693_8BIT, 0x3701, 0x14},
+ {OV5693_8BIT, 0x3702, 0xa0},
+ {OV5693_8BIT, 0x3703, 0xd8},
+ {OV5693_8BIT, 0x3704, 0x78},
+ {OV5693_8BIT, 0x3705, 0x02},
+ {OV5693_8BIT, 0x370a, 0x00},
+ {OV5693_8BIT, 0x370b, 0x20},
+ {OV5693_8BIT, 0x370c, 0x0c},
+ {OV5693_8BIT, 0x370d, 0x11},
+ {OV5693_8BIT, 0x370e, 0x00},
+ {OV5693_8BIT, 0x370f, 0x40},
+ {OV5693_8BIT, 0x3710, 0x00},
+ {OV5693_8BIT, 0x371a, 0x1c},
+ {OV5693_8BIT, 0x371b, 0x05},
+ {OV5693_8BIT, 0x371c, 0x01},
+ {OV5693_8BIT, 0x371e, 0xa1},
+ {OV5693_8BIT, 0x371f, 0x0c},
+ {OV5693_8BIT, 0x3721, 0x00},
+ {OV5693_8BIT, 0x3724, 0x10},
+ {OV5693_8BIT, 0x3726, 0x00},
+ {OV5693_8BIT, 0x372a, 0x01},
+ {OV5693_8BIT, 0x3730, 0x10},
+ {OV5693_8BIT, 0x3738, 0x22},
+ {OV5693_8BIT, 0x3739, 0xe5},
+ {OV5693_8BIT, 0x373a, 0x50},
+ {OV5693_8BIT, 0x373b, 0x02},
+ {OV5693_8BIT, 0x373c, 0x41},
+ {OV5693_8BIT, 0x373f, 0x02},
+ {OV5693_8BIT, 0x3740, 0x42},
+ {OV5693_8BIT, 0x3741, 0x02},
+ {OV5693_8BIT, 0x3742, 0x18},
+ {OV5693_8BIT, 0x3743, 0x01},
+ {OV5693_8BIT, 0x3744, 0x02},
+ {OV5693_8BIT, 0x3747, 0x10},
+ {OV5693_8BIT, 0x374c, 0x04},
+ {OV5693_8BIT, 0x3751, 0xf0},
+ {OV5693_8BIT, 0x3752, 0x00},
+ {OV5693_8BIT, 0x3753, 0x00},
+ {OV5693_8BIT, 0x3754, 0xc0},
+ {OV5693_8BIT, 0x3755, 0x00},
+ {OV5693_8BIT, 0x3756, 0x1a},
+ {OV5693_8BIT, 0x3758, 0x00},
+ {OV5693_8BIT, 0x3759, 0x0f},
+ {OV5693_8BIT, 0x376b, 0x44},
+ {OV5693_8BIT, 0x375c, 0x04},
+ {OV5693_8BIT, 0x3774, 0x10},
+ {OV5693_8BIT, 0x3776, 0x00},
+ {OV5693_8BIT, 0x377f, 0x08},
+ {OV5693_8BIT, 0x3780, 0x22},
+ {OV5693_8BIT, 0x3781, 0x0c},
+ {OV5693_8BIT, 0x3784, 0x2c},
+ {OV5693_8BIT, 0x3785, 0x1e},
+ {OV5693_8BIT, 0x378f, 0xf5},
+ {OV5693_8BIT, 0x3791, 0xb0},
+ {OV5693_8BIT, 0x3795, 0x00},
+ {OV5693_8BIT, 0x3796, 0x64},
+ {OV5693_8BIT, 0x3797, 0x11},
+ {OV5693_8BIT, 0x3798, 0x30},
+ {OV5693_8BIT, 0x3799, 0x41},
+ {OV5693_8BIT, 0x379a, 0x07},
+ {OV5693_8BIT, 0x379b, 0xb0},
+ {OV5693_8BIT, 0x379c, 0x0c},
+ {OV5693_8BIT, 0x37c5, 0x00},
+ {OV5693_8BIT, 0x37c6, 0x00},
+ {OV5693_8BIT, 0x37c7, 0x00},
+ {OV5693_8BIT, 0x37c9, 0x00},
+ {OV5693_8BIT, 0x37ca, 0x00},
+ {OV5693_8BIT, 0x37cb, 0x00},
+ {OV5693_8BIT, 0x37de, 0x00},
+ {OV5693_8BIT, 0x37df, 0x00},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3810, 0x00},
+ {OV5693_8BIT, 0x3812, 0x00},
+ {OV5693_8BIT, 0x3823, 0x00},
+ {OV5693_8BIT, 0x3824, 0x00},
+ {OV5693_8BIT, 0x3825, 0x00},
+ {OV5693_8BIT, 0x3826, 0x00},
+ {OV5693_8BIT, 0x3827, 0x00},
+ {OV5693_8BIT, 0x382a, 0x04},
+ {OV5693_8BIT, 0x3a04, 0x06},
+ {OV5693_8BIT, 0x3a05, 0x14},
+ {OV5693_8BIT, 0x3a06, 0x00},
+ {OV5693_8BIT, 0x3a07, 0xfe},
+ {OV5693_8BIT, 0x3b00, 0x00},
+ {OV5693_8BIT, 0x3b02, 0x00},
+ {OV5693_8BIT, 0x3b03, 0x00},
+ {OV5693_8BIT, 0x3b04, 0x00},
+ {OV5693_8BIT, 0x3b05, 0x00},
+ {OV5693_8BIT, 0x3e07, 0x20},
+ {OV5693_8BIT, 0x4000, 0x08},
+ {OV5693_8BIT, 0x4001, 0x04},
+ {OV5693_8BIT, 0x4002, 0x45},
+ {OV5693_8BIT, 0x4004, 0x08},
+ {OV5693_8BIT, 0x4005, 0x18},
+ {OV5693_8BIT, 0x4006, 0x20},
+ {OV5693_8BIT, 0x4008, 0x24},
+ {OV5693_8BIT, 0x4009, 0x10},
+ {OV5693_8BIT, 0x400c, 0x00},
+ {OV5693_8BIT, 0x400d, 0x00},
+ {OV5693_8BIT, 0x4058, 0x00},
+ {OV5693_8BIT, 0x404e, 0x37},
+ {OV5693_8BIT, 0x404f, 0x8f},
+ {OV5693_8BIT, 0x4058, 0x00},
+ {OV5693_8BIT, 0x4101, 0xb2},
+ {OV5693_8BIT, 0x4303, 0x00},
+ {OV5693_8BIT, 0x4304, 0x08},
+ {OV5693_8BIT, 0x4307, 0x31},
+ {OV5693_8BIT, 0x4311, 0x04},
+ {OV5693_8BIT, 0x4315, 0x01},
+ {OV5693_8BIT, 0x4511, 0x05},
+ {OV5693_8BIT, 0x4512, 0x01},
+ {OV5693_8BIT, 0x4806, 0x00},
+ {OV5693_8BIT, 0x4816, 0x52},
+ {OV5693_8BIT, 0x481f, 0x30},
+ {OV5693_8BIT, 0x4826, 0x2c},
+ {OV5693_8BIT, 0x4831, 0x64},
+ {OV5693_8BIT, 0x4d00, 0x04},
+ {OV5693_8BIT, 0x4d01, 0x71},
+ {OV5693_8BIT, 0x4d02, 0xfd},
+ {OV5693_8BIT, 0x4d03, 0xf5},
+ {OV5693_8BIT, 0x4d04, 0x0c},
+ {OV5693_8BIT, 0x4d05, 0xcc},
+ {OV5693_8BIT, 0x4837, 0x0a},
+ {OV5693_8BIT, 0x5000, 0x06},
+ {OV5693_8BIT, 0x5001, 0x01},
+ {OV5693_8BIT, 0x5003, 0x20},
+ {OV5693_8BIT, 0x5046, 0x0a},
+ {OV5693_8BIT, 0x5013, 0x00},
+ {OV5693_8BIT, 0x5046, 0x0a},
+ {OV5693_8BIT, 0x5780, 0x1c},
+ {OV5693_8BIT, 0x5786, 0x20},
+ {OV5693_8BIT, 0x5787, 0x10},
+ {OV5693_8BIT, 0x5788, 0x18},
+ {OV5693_8BIT, 0x578a, 0x04},
+ {OV5693_8BIT, 0x578b, 0x02},
+ {OV5693_8BIT, 0x578c, 0x02},
+ {OV5693_8BIT, 0x578e, 0x06},
+ {OV5693_8BIT, 0x578f, 0x02},
+ {OV5693_8BIT, 0x5790, 0x02},
+ {OV5693_8BIT, 0x5791, 0xff},
+ {OV5693_8BIT, 0x5842, 0x01},
+ {OV5693_8BIT, 0x5843, 0x2b},
+ {OV5693_8BIT, 0x5844, 0x01},
+ {OV5693_8BIT, 0x5845, 0x92},
+ {OV5693_8BIT, 0x5846, 0x01},
+ {OV5693_8BIT, 0x5847, 0x8f},
+ {OV5693_8BIT, 0x5848, 0x01},
+ {OV5693_8BIT, 0x5849, 0x0c},
+ {OV5693_8BIT, 0x5e00, 0x00},
+ {OV5693_8BIT, 0x5e10, 0x0c},
+ {OV5693_8BIT, 0x0100, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+#if ENABLE_NON_PREVIEW
+/*
+ * 654x496 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ */
+static struct ov5693_reg const ov5693_654x496[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0x90},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 1296x976 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ * DS from 2592x1952
+ */
+static struct ov5693_reg const ov5693_1296x976[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0x00},
+
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+
+ {OV5693_8BIT, 0x3808, 0x05},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x03},
+ {OV5693_8BIT, 0x380b, 0xD0},
+
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+
+ {OV5693_8BIT, 0x3810, 0x00},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3812, 0x00},
+ {OV5693_8BIT, 0x3813, 0x02},
+
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+
+};
+
+/*
+ * 336x256 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2564x1956
+ */
+static struct ov5693_reg const ov5693_336x256[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x01},
+ {OV5693_8BIT, 0x3809, 0x50},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0x00},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x1E},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 336x256 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2368x1956
+ */
+static struct ov5693_reg const ov5693_368x304[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3808, 0x01},
+ {OV5693_8BIT, 0x3809, 0x70},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0x30},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x80},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * ov5693_192x160 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2460x1956
+ */
+static struct ov5693_reg const ov5693_192x160[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x80},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+ {OV5693_8BIT, 0x3808, 0x00},
+ {OV5693_8BIT, 0x3809, 0xC0},
+ {OV5693_8BIT, 0x380a, 0x00},
+ {OV5693_8BIT, 0x380b, 0xA0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x40},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_736x496[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3803, 0x68},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0x3b},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0xe0},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0a}, /*hts*/
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /*vts*/
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+static struct ov5693_reg const ov5693_736x496[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0xe0},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0d},
+ {OV5693_8BIT, 0x380d, 0xb0},
+ {OV5693_8BIT, 0x380e, 0x05},
+ {OV5693_8BIT, 0x380f, 0xf2},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x01},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+*/
+/*
+ * 976x556 30fps 8.8ms VBlanking 2lane 10Bit (Scaling)
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_976x556[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x03},
+ {OV5693_8BIT, 0x3809, 0xd0},
+ {OV5693_8BIT, 0x380a, 0x02},
+ {OV5693_8BIT, 0x380b, 0x2C},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*DS from 2624x1492*/
+static struct ov5693_reg const ov5693_1296x736[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0x00},
+
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+
+ {OV5693_8BIT, 0x3808, 0x05},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x02},
+ {OV5693_8BIT, 0x380b, 0xe0},
+
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+
+ {OV5693_8BIT, 0x3813, 0xE8},
+
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_1636p_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x06},
+ {OV5693_8BIT, 0x3809, 0x64},
+ {OV5693_8BIT, 0x380a, 0x04},
+ {OV5693_8BIT, 0x380b, 0x48},
+ {OV5693_8BIT, 0x380c, 0x0a}, /*hts*/
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /*vts*/
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x02},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+static struct ov5693_reg const ov5693_1616x1216_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x80},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /*{3800,3801} Array X start*/
+ {OV5693_8BIT, 0x3801, 0x08}, /* 04 //{3800,3801} Array X start*/
+ {OV5693_8BIT, 0x3802, 0x00}, /*{3802,3803} Array Y start*/
+ {OV5693_8BIT, 0x3803, 0x04}, /* 00 //{3802,3803} Array Y start*/
+ {OV5693_8BIT, 0x3804, 0x0a}, /*{3804,3805} Array X end*/
+ {OV5693_8BIT, 0x3805, 0x37}, /* 3b //{3804,3805} Array X end*/
+ {OV5693_8BIT, 0x3806, 0x07}, /*{3806,3807} Array Y end*/
+ {OV5693_8BIT, 0x3807, 0x9f}, /* a3 //{3806,3807} Array Y end*/
+ {OV5693_8BIT, 0x3808, 0x06}, /*{3808,3809} Final output H size*/
+ {OV5693_8BIT, 0x3809, 0x50}, /*{3808,3809} Final output H size*/
+ {OV5693_8BIT, 0x380a, 0x04}, /*{380a,380b} Final output V size*/
+ {OV5693_8BIT, 0x380b, 0xc0}, /*{380a,380b} Final output V size*/
+ {OV5693_8BIT, 0x380c, 0x0a}, /*{380c,380d} HTS*/
+ {OV5693_8BIT, 0x380d, 0x80}, /*{380c,380d} HTS*/
+ {OV5693_8BIT, 0x380e, 0x07}, /*{380e,380f} VTS*/
+ {OV5693_8BIT, 0x380f, 0xc0}, /* bc //{380e,380f} VTS*/
+ {OV5693_8BIT, 0x3810, 0x00}, /*{3810,3811} windowing X offset*/
+ {OV5693_8BIT, 0x3811, 0x10}, /*{3810,3811} windowing X offset*/
+ {OV5693_8BIT, 0x3812, 0x00}, /*{3812,3813} windowing Y offset*/
+ {OV5693_8BIT, 0x3813, 0x06}, /*{3812,3813} windowing Y offset*/
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00}, /*FLIP/Binnning control*/
+ {OV5693_8BIT, 0x3821, 0x1e}, /*MIRROR control*/
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 1940x1096 30fps 8.8ms VBlanking 2lane 10bit (Scaling)
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_1940x1096[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x07},
+ {OV5693_8BIT, 0x3809, 0x94},
+ {OV5693_8BIT, 0x380a, 0x04},
+ {OV5693_8BIT, 0x380b, 0x48},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x02},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_2592x1456_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa4},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x20},
+ {OV5693_8BIT, 0x380a, 0x05},
+ {OV5693_8BIT, 0x380b, 0xb0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+static struct ov5693_reg const ov5693_2576x1456_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa4},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x05},
+ {OV5693_8BIT, 0x380b, 0xb0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x18},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 2592x1944 30fps 0.6ms VBlanking 2lane 10Bit
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_2592x1944_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x20},
+ {OV5693_8BIT, 0x380a, 0x07},
+ {OV5693_8BIT, 0x380b, 0x98},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+ * 11:9 Full FOV Output, expected FOV Res: 2346x1920
+ * ISP Effect Res: 1408x1152
+ * Sensor out: 1424x1168, DS From: 2380x1952
+ *
+ * WA: Left Offset: 8, Hor scal: 64
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_1424x1168_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x3b}, /* long exposure[15:8] */
+ {OV5693_8BIT, 0x3502, 0x80}, /* long exposure[7:0] */
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /* TIMING_X_ADDR_START */
+ {OV5693_8BIT, 0x3801, 0x50}, /* 80 */
+ {OV5693_8BIT, 0x3802, 0x00}, /* TIMING_Y_ADDR_START */
+ {OV5693_8BIT, 0x3803, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3804, 0x09}, /* TIMING_X_ADDR_END */
+ {OV5693_8BIT, 0x3805, 0xdd}, /* 2525 */
+ {OV5693_8BIT, 0x3806, 0x07}, /* TIMING_Y_ADDR_END */
+ {OV5693_8BIT, 0x3807, 0xa1}, /* 1953 */
+ {OV5693_8BIT, 0x3808, 0x05}, /* TIMING_X_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x3809, 0x90}, /* 1424 */
+ {OV5693_8BIT, 0x380a, 0x04}, /* TIMING_Y_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x380b, 0x90}, /* 1168 */
+ {OV5693_8BIT, 0x380c, 0x0a}, /* TIMING_HTS */
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /* TIMING_VTS */
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3810, 0x00}, /* TIMING_ISP_X_WIN */
+ {OV5693_8BIT, 0x3811, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3812, 0x00}, /* TIMING_ISP_Y_WIN */
+ {OV5693_8BIT, 0x3813, 0x00}, /* 0 */
+ {OV5693_8BIT, 0x3814, 0x11}, /* TIME_X_INC */
+ {OV5693_8BIT, 0x3815, 0x11}, /* TIME_Y_INC */
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+ * 3:2 Full FOV Output, expected FOV Res: 2560x1706
+ * ISP Effect Res: 720x480
+ * Sensor out: 736x496, DS From 2616x1764
+ */
+static struct ov5693_reg const ov5693_736x496_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x3b}, /* long exposure[15:8] */
+ {OV5693_8BIT, 0x3502, 0x80}, /* long exposure[7:0] */
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /* TIMING_X_ADDR_START */
+ {OV5693_8BIT, 0x3801, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3802, 0x00}, /* TIMING_Y_ADDR_START */
+ {OV5693_8BIT, 0x3803, 0x62}, /* 98 */
+ {OV5693_8BIT, 0x3804, 0x0a}, /* TIMING_X_ADDR_END */
+ {OV5693_8BIT, 0x3805, 0x3b}, /* 2619 */
+ {OV5693_8BIT, 0x3806, 0x07}, /* TIMING_Y_ADDR_END */
+ {OV5693_8BIT, 0x3807, 0x43}, /* 1859 */
+ {OV5693_8BIT, 0x3808, 0x02}, /* TIMING_X_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x3809, 0xe0}, /* 736 */
+ {OV5693_8BIT, 0x380a, 0x01}, /* TIMING_Y_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x380b, 0xf0}, /* 496 */
+ {OV5693_8BIT, 0x380c, 0x0a}, /* TIMING_HTS */
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /* TIMING_VTS */
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3810, 0x00}, /* TIMING_ISP_X_WIN */
+ {OV5693_8BIT, 0x3811, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3812, 0x00}, /* TIMING_ISP_Y_WIN */
+ {OV5693_8BIT, 0x3813, 0x00}, /* 0 */
+ {OV5693_8BIT, 0x3814, 0x11}, /* TIME_X_INC */
+ {OV5693_8BIT, 0x3815, 0x11}, /* TIME_Y_INC */
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_2576x1936_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x07},
+ {OV5693_8BIT, 0x380b, 0x90},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x18},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_resolution ov5693_res_preview[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_736x496_30fps,
+ },
+ {
+ .desc = "ov5693_1616x1216_30fps",
+ .width = 1616,
+ .height = 1216,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1616x1216_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2576,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2576x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2576,
+ .height = 1936,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2576x1936_30fps,
+ },
+};
+
+#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview))
+
+/*
+ * Disable non-preview configurations until the configuration selection is
+ * improved.
+ */
+#if ENABLE_NON_PREVIEW
+struct ov5693_resolution ov5693_res_still[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_736x496_30fps,
+ },
+ {
+ .desc = "ov5693_1424x1168_30fps",
+ .width = 1424,
+ .height = 1168,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1424x1168_30fps,
+ },
+ {
+ .desc = "ov5693_1616x1216_30fps",
+ .width = 1616,
+ .height = 1216,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1616x1216_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1944,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1944_30fps,
+ },
+};
+
+#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still))
+
+struct ov5693_resolution ov5693_res_video[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_736x496,
+ },
+ {
+ .desc = "ov5693_336x256_30fps",
+ .width = 336,
+ .height = 256,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_336x256,
+ },
+ {
+ .desc = "ov5693_368x304_30fps",
+ .width = 368,
+ .height = 304,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_368x304,
+ },
+ {
+ .desc = "ov5693_192x160_30fps",
+ .width = 192,
+ .height = 160,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_192x160,
+ },
+ {
+ .desc = "ov5693_1296x736_30fps",
+ .width = 1296,
+ .height = 736,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 0,
+ .regs = ov5693_1296x736,
+ },
+ {
+ .desc = "ov5693_1296x976_30fps",
+ .width = 1296,
+ .height = 976,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 0,
+ .regs = ov5693_1296x976,
+ },
+ {
+ .desc = "ov5693_1636P_30fps",
+ .width = 1636,
+ .height = 1096,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1636p_30fps,
+ },
+ {
+ .desc = "ov5693_1080P_30fps",
+ .width = 1940,
+ .height = 1096,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1940x1096,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1944,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1944_30fps,
+ },
+};
+
+#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video))
+#endif
+
+static struct ov5693_resolution *ov5693_res = ov5693_res_preview;
+static unsigned long N_RES = N_RES_PREVIEW;
+#endif
--
2.30.0
From 2e5b7ad9aa07ba9431afe9ebbc743f4b6e026953 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabian=20W=C3=BCthrich?= <me@fabwu.ch>
Date: Mon, 28 Dec 2020 22:49:17 +0100
Subject: [PATCH] ov5693: Add user controls to flip image
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 63 ++++++++++++++++++++++++++++++++++++++
drivers/media/i2c/ov5693.h | 10 ++++++
2 files changed, 73 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index d6e20909d35e..1d3ee422f37a 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -831,6 +831,29 @@ static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
return ov5693_t_focus_abs(sd, dev->focus + value);
}
+static int ov5693_update_hflip(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ u8 hflip = dev->hflip ? OV5693_TIMING_REG21_FLIP :
+ OV5693_TIMING_REG21_NORMAL;
+
+ return ov5693_write_reg(client, OV5693_8BIT, OV5693_TIMING_REG21,
+ hflip);
+}
+
+static int ov5693_update_vflip(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ u8 vflip = dev->vflip ? OV5693_TIMING_REG20_FLIP :
+ OV5693_TIMING_REG20_NORMAL;
+ return ov5693_write_reg(client, OV5693_8BIT, OV5693_TIMING_REG20,
+ vflip);
+}
+
#define DELAY_PER_STEP_NS 1000000
#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
@@ -852,6 +875,18 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
__func__, ctrl->val);
ret = ov5693_t_focus_rel(&dev->sd, ctrl->val);
break;
+ case V4L2_CID_HFLIP:
+ dev_info(&client->dev, "%s: CID_HFLIP:%d.\n", __func__,
+ ctrl->val);
+ dev->hflip = ctrl->val;
+ ret = ov5693_update_hflip(&dev->sd);
+ break;
+ case V4L2_CID_VFLIP:
+ dev_info(&client->dev, "%s: CID_VFLIP:%d.\n", __func__,
+ ctrl->val);
+ dev->vflip = ctrl->val;
+ ret = ov5693_update_vflip(&dev->sd);
+ break;
default:
ret = -EINVAL;
}
@@ -917,6 +952,22 @@ static const struct v4l2_ctrl_config ov5693_controls[] = {
.def = 0,
.flags = 0,
},
+ {
+ .ops = &ctrl_ops,
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "sensor flipped horizontally",
+ .max = 1,
+ .step = 1,
+ },
+ {
+ .ops = &ctrl_ops,
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "sensor flipped vertically",
+ .max = 1,
+ .step = 1,
+ },
};
static int ov5693_init(struct v4l2_subdev *sd)
@@ -1147,6 +1198,18 @@ static int startup(struct v4l2_subdev *sd)
return ret;
}
+ ret = ov5693_update_hflip(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 hflip err.\n");
+ return ret;
+ }
+
+ ret = ov5693_update_vflip(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 vflip err.\n");
+ return ret;
+ }
+
return ret;
}
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 839b0773e2bc..e788a8068f50 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -136,6 +136,13 @@
#define OV5693_MWB_BLUE_GAIN_H 0x3404
#define OV5693_MWB_GAIN_MAX 0x0fff
+#define OV5693_TIMING_REG21 0x3821 /* horizontal flip */
+#define OV5693_TIMING_REG21_NORMAL 0x18
+#define OV5693_TIMING_REG21_FLIP 0x1E
+#define OV5693_TIMING_REG20 0x3820 /* vertical flip */
+#define OV5693_TIMING_REG20_NORMAL 0x10
+#define OV5693_TIMING_REG20_FLIP 0x16
+
#define OV5693_START_STREAMING 0x01
#define OV5693_STOP_STREAMING 0x00
@@ -261,6 +268,9 @@ struct ov5693_device {
bool vcm_update;
enum vcm_type vcm;
+ bool hflip;
+ bool vflip;
+
bool has_vcm;
};
--
2.30.0