Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: i2c: gc2145: Galaxy Core GC2145 sensor support

Addition of support for the Galaxy Core GC2145 XVGA sensor.
The sensor supports both DVP and CSI-2 interfaces however for
the time being only CSI-2 is implemented.

Configurations are currently based on initialization scripts
coming from Galaxy Core and so for that purpose only 3 static
resolutions are supported:
- 640x480
- 1280x720
- 1600x1200

Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

authored by

Alain Volmat and committed by
Hans Verkuil
03cc7fef 0d32f666

+1465
+8
MAINTAINERS
··· 8760 8760 F: tools/perf/bench/futex* 8761 8761 F: tools/testing/selftests/futex/ 8762 8762 8763 + GALAXYCORE GC2145 SENSOR DRIVER 8764 + M: Alain Volmat <alain.volmat@foss.st.com> 8765 + L: linux-media@vger.kernel.org 8766 + S: Maintained 8767 + T: git git://linuxtv.org/media_tree.git 8768 + F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml 8769 + F: drivers/media/i2c/gc2145.c 8770 + 8763 8771 GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER 8764 8772 M: Tim Harvey <tharvey@gateworks.com> 8765 8773 S: Maintained
+10
drivers/media/i2c/Kconfig
··· 50 50 To compile this driver as a module, choose M here: the 51 51 module will be called ar0521. 52 52 53 + config VIDEO_GC2145 54 + select V4L2_CCI_I2C 55 + tristate "GalaxyCore GC2145 sensor support" 56 + help 57 + This is a V4L2 sensor-level driver for GalaxyCore GC2145 58 + 2 Mpixel camera. 59 + 60 + To compile this driver as a module, choose M here: the 61 + module will be called gc2145. 62 + 53 63 config VIDEO_HI556 54 64 tristate "Hynix Hi-556 sensor support" 55 65 help
+1
drivers/media/i2c/Makefile
··· 36 36 obj-$(CONFIG_VIDEO_DW9768) += dw9768.o 37 37 obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o 38 38 obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ 39 + obj-$(CONFIG_VIDEO_GC2145) += gc2145.o 39 40 obj-$(CONFIG_VIDEO_HI556) += hi556.o 40 41 obj-$(CONFIG_VIDEO_HI846) += hi846.o 41 42 obj-$(CONFIG_VIDEO_HI847) += hi847.o
+1446
drivers/media/i2c/gc2145.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * A V4L2 driver for Galaxycore GC2145 camera. 4 + * Copyright (C) 2023, STMicroelectronics SA 5 + * 6 + * Inspired by the imx219.c driver 7 + * 8 + * Datasheet v1.0 available at http://files.pine64.org/doc/datasheet/PinebookPro/GC2145%20CSP%20DataSheet%20release%20V1.0_20131201.pdf 9 + */ 10 + 11 + #include <linux/clk.h> 12 + #include <linux/delay.h> 13 + #include <linux/gpio/consumer.h> 14 + #include <linux/i2c.h> 15 + #include <linux/module.h> 16 + #include <linux/pm_runtime.h> 17 + #include <linux/regulator/consumer.h> 18 + #include <linux/units.h> 19 + 20 + #include <media/mipi-csi2.h> 21 + #include <media/v4l2-cci.h> 22 + #include <media/v4l2-ctrls.h> 23 + #include <media/v4l2-device.h> 24 + #include <media/v4l2-event.h> 25 + #include <media/v4l2-fwnode.h> 26 + #include <media/v4l2-mediabus.h> 27 + 28 + /* Chip ID */ 29 + #define GC2145_CHIP_ID 0x2145 30 + 31 + /* Page 0 */ 32 + #define GC2145_REG_EXPOSURE CCI_REG16(0x03) 33 + #define GC2145_REG_HBLANK CCI_REG16(0x05) 34 + #define GC2145_REG_VBLANK CCI_REG16(0x07) 35 + #define GC2145_REG_ROW_START CCI_REG16(0x09) 36 + #define GC2145_REG_COL_START CCI_REG16(0x0b) 37 + #define GC2145_REG_WIN_HEIGHT CCI_REG16(0x0d) 38 + #define GC2145_REG_WIN_WIDTH CCI_REG16(0x0f) 39 + #define GC2145_REG_ANALOG_MODE1 CCI_REG8(0x17) 40 + #define GC2145_REG_OUTPUT_FMT CCI_REG8(0x84) 41 + #define GC2145_REG_SYNC_MODE CCI_REG8(0x86) 42 + #define GC2145_SYNC_MODE_COL_SWITCH BIT(4) 43 + #define GC2145_SYNC_MODE_ROW_SWITCH BIT(5) 44 + #define GC2145_REG_BYPASS_MODE CCI_REG8(0x89) 45 + #define GC2145_BYPASS_MODE_SWITCH BIT(5) 46 + #define GC2145_REG_DEBUG_MODE2 CCI_REG8(0x8c) 47 + #define GC2145_REG_DEBUG_MODE3 CCI_REG8(0x8d) 48 + #define GC2145_REG_CROP_ENABLE CCI_REG8(0x90) 49 + #define GC2145_REG_CROP_Y CCI_REG16(0x91) 50 + #define GC2145_REG_CROP_X CCI_REG16(0x93) 51 + #define GC2145_REG_CROP_HEIGHT CCI_REG16(0x95) 52 + #define GC2145_REG_CROP_WIDTH CCI_REG16(0x97) 53 + #define GC2145_REG_GLOBAL_GAIN CCI_REG8(0xb0) 54 + #define GC2145_REG_CHIP_ID CCI_REG16(0xf0) 55 + #define GC2145_REG_PAD_IO CCI_REG8(0xf2) 56 + #define GC2145_REG_PAGE_SELECT CCI_REG8(0xfe) 57 + /* Page 3 */ 58 + #define GC2145_REG_DPHY_ANALOG_MODE1 CCI_REG8(0x01) 59 + #define GC2145_DPHY_MODE_PHY_CLK_EN BIT(0) 60 + #define GC2145_DPHY_MODE_PHY_LANE0_EN BIT(1) 61 + #define GC2145_DPHY_MODE_PHY_LANE1_EN BIT(2) 62 + #define GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL BIT(7) 63 + #define GC2145_REG_DPHY_ANALOG_MODE2 CCI_REG8(0x02) 64 + #define GC2145_DPHY_CLK_DIFF(a) ((a) & 0x07) 65 + #define GC2145_DPHY_LANE0_DIFF(a) (((a) & 0x07) << 4) 66 + #define GC2145_REG_DPHY_ANALOG_MODE3 CCI_REG8(0x03) 67 + #define GC2145_DPHY_LANE1_DIFF(a) ((a) & 0x07) 68 + #define GC2145_DPHY_CLK_DELAY BIT(4) 69 + #define GC2145_DPHY_LANE0_DELAY BIT(5) 70 + #define GC2145_DPHY_LANE1_DELAY BIT(6) 71 + #define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) 72 + #define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) 73 + #define GC2145_REG_FIFO_MODE CCI_REG8(0x06) 74 + #define GC2145_FIFO_MODE_READ_GATE BIT(3) 75 + #define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) 76 + #define GC2145_REG_BUF_CSI2_MODE CCI_REG8(0x10) 77 + #define GC2145_CSI2_MODE_DOUBLE BIT(0) 78 + #define GC2145_CSI2_MODE_RAW8 BIT(2) 79 + #define GC2145_CSI2_MODE_MIPI_EN BIT(4) 80 + #define GC2145_CSI2_MODE_EN BIT(7) 81 + #define GC2145_REG_MIPI_DT CCI_REG8(0x11) 82 + #define GC2145_REG_LWC_LOW CCI_REG8(0x12) 83 + #define GC2145_REG_LWC_HIGH CCI_REG8(0x13) 84 + #define GC2145_REG_DPHY_MODE CCI_REG8(0x15) 85 + #define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) 86 + #define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) 87 + #define GC2145_REG_T_LPX CCI_REG8(0x21) 88 + #define GC2145_REG_T_CLK_HS_PREPARE CCI_REG8(0x22) 89 + #define GC2145_REG_T_CLK_ZERO CCI_REG8(0x23) 90 + #define GC2145_REG_T_CLK_PRE CCI_REG8(0x24) 91 + #define GC2145_REG_T_CLK_POST CCI_REG8(0x25) 92 + #define GC2145_REG_T_CLK_TRAIL CCI_REG8(0x26) 93 + #define GC2145_REG_T_HS_EXIT CCI_REG8(0x27) 94 + #define GC2145_REG_T_WAKEUP CCI_REG8(0x28) 95 + #define GC2145_REG_T_HS_PREPARE CCI_REG8(0x29) 96 + #define GC2145_REG_T_HS_ZERO CCI_REG8(0x2a) 97 + #define GC2145_REG_T_HS_TRAIL CCI_REG8(0x2b) 98 + 99 + /* External clock frequency is 24.0MHz */ 100 + #define GC2145_XCLK_FREQ (24 * HZ_PER_MHZ) 101 + 102 + #define GC2145_NATIVE_WIDTH 1616U 103 + #define GC2145_NATIVE_HEIGHT 1232U 104 + 105 + /** 106 + * struct gc2145_mode - GC2145 mode description 107 + * @width: frame width (in pixels) 108 + * @height: frame height (in pixels) 109 + * @reg_seq: registers config sequence to enter into the mode 110 + * @reg_seq_size: size of the sequence 111 + * @pixel_rate: pixel rate associated with the mode 112 + * @crop: window area captured 113 + * @hblank: default horizontal blanking 114 + * @vblank: default vertical blanking 115 + * @link_freq_index: index within the link frequency menu 116 + */ 117 + struct gc2145_mode { 118 + unsigned int width; 119 + unsigned int height; 120 + const struct cci_reg_sequence *reg_seq; 121 + size_t reg_seq_size; 122 + unsigned long pixel_rate; 123 + struct v4l2_rect crop; 124 + unsigned int hblank; 125 + unsigned int vblank; 126 + unsigned int link_freq_index; 127 + }; 128 + 129 + #define GC2145_DEFAULT_EXPOSURE 0x04e2 130 + #define GC2145_DEFAULT_GLOBAL_GAIN 0x55 131 + static const struct cci_reg_sequence gc2145_common_regs[] = { 132 + {GC2145_REG_PAGE_SELECT, 0x00}, 133 + /* SH Delay */ 134 + {CCI_REG8(0x12), 0x2e}, 135 + /* Flip */ 136 + {GC2145_REG_ANALOG_MODE1, 0x14}, 137 + /* Analog Conf */ 138 + {CCI_REG8(0x18), 0x22}, {CCI_REG8(0x19), 0x0e}, {CCI_REG8(0x1a), 0x01}, 139 + {CCI_REG8(0x1b), 0x4b}, {CCI_REG8(0x1c), 0x07}, {CCI_REG8(0x1d), 0x10}, 140 + {CCI_REG8(0x1e), 0x88}, {CCI_REG8(0x1f), 0x78}, {CCI_REG8(0x20), 0x03}, 141 + {CCI_REG8(0x21), 0x40}, {CCI_REG8(0x22), 0xa0}, {CCI_REG8(0x24), 0x16}, 142 + {CCI_REG8(0x25), 0x01}, {CCI_REG8(0x26), 0x10}, {CCI_REG8(0x2d), 0x60}, 143 + {CCI_REG8(0x30), 0x01}, {CCI_REG8(0x31), 0x90}, {CCI_REG8(0x33), 0x06}, 144 + {CCI_REG8(0x34), 0x01}, 145 + /* ISP related */ 146 + {CCI_REG8(0x80), 0x7f}, {CCI_REG8(0x81), 0x26}, {CCI_REG8(0x82), 0xfa}, 147 + {CCI_REG8(0x83), 0x00}, {CCI_REG8(0x84), 0x02}, {CCI_REG8(0x86), 0x02}, 148 + {CCI_REG8(0x88), 0x03}, 149 + {GC2145_REG_BYPASS_MODE, 0x03}, 150 + {CCI_REG8(0x85), 0x08}, {CCI_REG8(0x8a), 0x00}, {CCI_REG8(0x8b), 0x00}, 151 + {GC2145_REG_GLOBAL_GAIN, GC2145_DEFAULT_GLOBAL_GAIN}, 152 + {CCI_REG8(0xc3), 0x00}, {CCI_REG8(0xc4), 0x80}, {CCI_REG8(0xc5), 0x90}, 153 + {CCI_REG8(0xc6), 0x3b}, {CCI_REG8(0xc7), 0x46}, 154 + /* BLK */ 155 + {GC2145_REG_PAGE_SELECT, 0x00}, 156 + {CCI_REG8(0x40), 0x42}, {CCI_REG8(0x41), 0x00}, {CCI_REG8(0x43), 0x5b}, 157 + {CCI_REG8(0x5e), 0x00}, {CCI_REG8(0x5f), 0x00}, {CCI_REG8(0x60), 0x00}, 158 + {CCI_REG8(0x61), 0x00}, {CCI_REG8(0x62), 0x00}, {CCI_REG8(0x63), 0x00}, 159 + {CCI_REG8(0x64), 0x00}, {CCI_REG8(0x65), 0x00}, {CCI_REG8(0x66), 0x20}, 160 + {CCI_REG8(0x67), 0x20}, {CCI_REG8(0x68), 0x20}, {CCI_REG8(0x69), 0x20}, 161 + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x6a), 0x08}, {CCI_REG8(0x6b), 0x08}, 162 + {CCI_REG8(0x6c), 0x08}, {CCI_REG8(0x6d), 0x08}, {CCI_REG8(0x6e), 0x08}, 163 + {CCI_REG8(0x6f), 0x08}, {CCI_REG8(0x70), 0x08}, {CCI_REG8(0x71), 0x08}, 164 + {CCI_REG8(0x76), 0x00}, {CCI_REG8(0x72), 0xf0}, {CCI_REG8(0x7e), 0x3c}, 165 + {CCI_REG8(0x7f), 0x00}, 166 + {GC2145_REG_PAGE_SELECT, 0x02}, 167 + {CCI_REG8(0x48), 0x15}, {CCI_REG8(0x49), 0x00}, {CCI_REG8(0x4b), 0x0b}, 168 + /* AEC */ 169 + {GC2145_REG_PAGE_SELECT, 0x00}, 170 + {GC2145_REG_EXPOSURE, GC2145_DEFAULT_EXPOSURE}, 171 + {GC2145_REG_PAGE_SELECT, 0x01}, 172 + {CCI_REG8(0x01), 0x04}, {CCI_REG8(0x02), 0xc0}, {CCI_REG8(0x03), 0x04}, 173 + {CCI_REG8(0x04), 0x90}, {CCI_REG8(0x05), 0x30}, {CCI_REG8(0x06), 0x90}, 174 + {CCI_REG8(0x07), 0x30}, {CCI_REG8(0x08), 0x80}, {CCI_REG8(0x09), 0x00}, 175 + {CCI_REG8(0x0a), 0x82}, {CCI_REG8(0x0b), 0x11}, {CCI_REG8(0x0c), 0x10}, 176 + {CCI_REG8(0x11), 0x10}, {CCI_REG8(0x13), 0x7b}, {CCI_REG8(0x17), 0x00}, 177 + {CCI_REG8(0x1c), 0x11}, {CCI_REG8(0x1e), 0x61}, {CCI_REG8(0x1f), 0x35}, 178 + {CCI_REG8(0x20), 0x40}, {CCI_REG8(0x22), 0x40}, {CCI_REG8(0x23), 0x20}, 179 + {GC2145_REG_PAGE_SELECT, 0x02}, 180 + {CCI_REG8(0x0f), 0x04}, 181 + {GC2145_REG_PAGE_SELECT, 0x01}, 182 + {CCI_REG8(0x12), 0x35}, {CCI_REG8(0x15), 0xb0}, {CCI_REG8(0x10), 0x31}, 183 + {CCI_REG8(0x3e), 0x28}, {CCI_REG8(0x3f), 0xb0}, {CCI_REG8(0x40), 0x90}, 184 + {CCI_REG8(0x41), 0x0f}, 185 + /* INTPEE */ 186 + {GC2145_REG_PAGE_SELECT, 0x02}, 187 + {CCI_REG8(0x90), 0x6c}, {CCI_REG8(0x91), 0x03}, {CCI_REG8(0x92), 0xcb}, 188 + {CCI_REG8(0x94), 0x33}, {CCI_REG8(0x95), 0x84}, {CCI_REG8(0x97), 0x65}, 189 + {CCI_REG8(0xa2), 0x11}, 190 + /* DNDD */ 191 + {GC2145_REG_PAGE_SELECT, 0x02}, 192 + {CCI_REG8(0x80), 0xc1}, {CCI_REG8(0x81), 0x08}, {CCI_REG8(0x82), 0x05}, 193 + {CCI_REG8(0x83), 0x08}, {CCI_REG8(0x84), 0x0a}, {CCI_REG8(0x86), 0xf0}, 194 + {CCI_REG8(0x87), 0x50}, {CCI_REG8(0x88), 0x15}, {CCI_REG8(0x89), 0xb0}, 195 + {CCI_REG8(0x8a), 0x30}, {CCI_REG8(0x8b), 0x10}, 196 + /* ASDE */ 197 + {GC2145_REG_PAGE_SELECT, 0x01}, 198 + {CCI_REG8(0x21), 0x04}, 199 + {GC2145_REG_PAGE_SELECT, 0x02}, 200 + {CCI_REG8(0xa3), 0x50}, {CCI_REG8(0xa4), 0x20}, {CCI_REG8(0xa5), 0x40}, 201 + {CCI_REG8(0xa6), 0x80}, {CCI_REG8(0xab), 0x40}, {CCI_REG8(0xae), 0x0c}, 202 + {CCI_REG8(0xb3), 0x46}, {CCI_REG8(0xb4), 0x64}, {CCI_REG8(0xb6), 0x38}, 203 + {CCI_REG8(0xb7), 0x01}, {CCI_REG8(0xb9), 0x2b}, {CCI_REG8(0x3c), 0x04}, 204 + {CCI_REG8(0x3d), 0x15}, {CCI_REG8(0x4b), 0x06}, {CCI_REG8(0x4c), 0x20}, 205 + /* Gamma */ 206 + {GC2145_REG_PAGE_SELECT, 0x02}, 207 + {CCI_REG8(0x10), 0x09}, {CCI_REG8(0x11), 0x0d}, {CCI_REG8(0x12), 0x13}, 208 + {CCI_REG8(0x13), 0x19}, {CCI_REG8(0x14), 0x27}, {CCI_REG8(0x15), 0x37}, 209 + {CCI_REG8(0x16), 0x45}, {CCI_REG8(0x17), 0x53}, {CCI_REG8(0x18), 0x69}, 210 + {CCI_REG8(0x19), 0x7d}, {CCI_REG8(0x1a), 0x8f}, {CCI_REG8(0x1b), 0x9d}, 211 + {CCI_REG8(0x1c), 0xa9}, {CCI_REG8(0x1d), 0xbd}, {CCI_REG8(0x1e), 0xcd}, 212 + {CCI_REG8(0x1f), 0xd9}, {CCI_REG8(0x20), 0xe3}, {CCI_REG8(0x21), 0xea}, 213 + {CCI_REG8(0x22), 0xef}, {CCI_REG8(0x23), 0xf5}, {CCI_REG8(0x24), 0xf9}, 214 + {CCI_REG8(0x25), 0xff}, 215 + {GC2145_REG_PAGE_SELECT, 0x00}, 216 + {CCI_REG8(0xc6), 0x20}, {CCI_REG8(0xc7), 0x2b}, 217 + /* Gamma 2 */ 218 + {GC2145_REG_PAGE_SELECT, 0x02}, 219 + {CCI_REG8(0x26), 0x0f}, {CCI_REG8(0x27), 0x14}, {CCI_REG8(0x28), 0x19}, 220 + {CCI_REG8(0x29), 0x1e}, {CCI_REG8(0x2a), 0x27}, {CCI_REG8(0x2b), 0x33}, 221 + {CCI_REG8(0x2c), 0x3b}, {CCI_REG8(0x2d), 0x45}, {CCI_REG8(0x2e), 0x59}, 222 + {CCI_REG8(0x2f), 0x69}, {CCI_REG8(0x30), 0x7c}, {CCI_REG8(0x31), 0x89}, 223 + {CCI_REG8(0x32), 0x98}, {CCI_REG8(0x33), 0xae}, {CCI_REG8(0x34), 0xc0}, 224 + {CCI_REG8(0x35), 0xcf}, {CCI_REG8(0x36), 0xda}, {CCI_REG8(0x37), 0xe2}, 225 + {CCI_REG8(0x38), 0xe9}, {CCI_REG8(0x39), 0xf3}, {CCI_REG8(0x3a), 0xf9}, 226 + {CCI_REG8(0x3b), 0xff}, 227 + /* YCP */ 228 + {GC2145_REG_PAGE_SELECT, 0x02}, 229 + {CCI_REG8(0xd1), 0x32}, {CCI_REG8(0xd2), 0x32}, {CCI_REG8(0xd3), 0x40}, 230 + {CCI_REG8(0xd6), 0xf0}, {CCI_REG8(0xd7), 0x10}, {CCI_REG8(0xd8), 0xda}, 231 + {CCI_REG8(0xdd), 0x14}, {CCI_REG8(0xde), 0x86}, {CCI_REG8(0xed), 0x80}, 232 + {CCI_REG8(0xee), 0x00}, {CCI_REG8(0xef), 0x3f}, {CCI_REG8(0xd8), 0xd8}, 233 + /* ABS */ 234 + {GC2145_REG_PAGE_SELECT, 0x01}, 235 + {CCI_REG8(0x9f), 0x40}, 236 + /* LSC */ 237 + {GC2145_REG_PAGE_SELECT, 0x01}, 238 + {CCI_REG8(0xc2), 0x14}, {CCI_REG8(0xc3), 0x0d}, {CCI_REG8(0xc4), 0x0c}, 239 + {CCI_REG8(0xc8), 0x15}, {CCI_REG8(0xc9), 0x0d}, {CCI_REG8(0xca), 0x0a}, 240 + {CCI_REG8(0xbc), 0x24}, {CCI_REG8(0xbd), 0x10}, {CCI_REG8(0xbe), 0x0b}, 241 + {CCI_REG8(0xb6), 0x25}, {CCI_REG8(0xb7), 0x16}, {CCI_REG8(0xb8), 0x15}, 242 + {CCI_REG8(0xc5), 0x00}, {CCI_REG8(0xc6), 0x00}, {CCI_REG8(0xc7), 0x00}, 243 + {CCI_REG8(0xcb), 0x00}, {CCI_REG8(0xcc), 0x00}, {CCI_REG8(0xcd), 0x00}, 244 + {CCI_REG8(0xbf), 0x07}, {CCI_REG8(0xc0), 0x00}, {CCI_REG8(0xc1), 0x00}, 245 + {CCI_REG8(0xb9), 0x00}, {CCI_REG8(0xba), 0x00}, {CCI_REG8(0xbb), 0x00}, 246 + {CCI_REG8(0xaa), 0x01}, {CCI_REG8(0xab), 0x01}, {CCI_REG8(0xac), 0x00}, 247 + {CCI_REG8(0xad), 0x05}, {CCI_REG8(0xae), 0x06}, {CCI_REG8(0xaf), 0x0e}, 248 + {CCI_REG8(0xb0), 0x0b}, {CCI_REG8(0xb1), 0x07}, {CCI_REG8(0xb2), 0x06}, 249 + {CCI_REG8(0xb3), 0x17}, {CCI_REG8(0xb4), 0x0e}, {CCI_REG8(0xb5), 0x0e}, 250 + {CCI_REG8(0xd0), 0x09}, {CCI_REG8(0xd1), 0x00}, {CCI_REG8(0xd2), 0x00}, 251 + {CCI_REG8(0xd6), 0x08}, {CCI_REG8(0xd7), 0x00}, {CCI_REG8(0xd8), 0x00}, 252 + {CCI_REG8(0xd9), 0x00}, {CCI_REG8(0xda), 0x00}, {CCI_REG8(0xdb), 0x00}, 253 + {CCI_REG8(0xd3), 0x0a}, {CCI_REG8(0xd4), 0x00}, {CCI_REG8(0xd5), 0x00}, 254 + {CCI_REG8(0xa4), 0x00}, {CCI_REG8(0xa5), 0x00}, {CCI_REG8(0xa6), 0x77}, 255 + {CCI_REG8(0xa7), 0x77}, {CCI_REG8(0xa8), 0x77}, {CCI_REG8(0xa9), 0x77}, 256 + {CCI_REG8(0xa1), 0x80}, {CCI_REG8(0xa2), 0x80}, 257 + {GC2145_REG_PAGE_SELECT, 0x01}, 258 + {CCI_REG8(0xdf), 0x0d}, {CCI_REG8(0xdc), 0x25}, {CCI_REG8(0xdd), 0x30}, 259 + {CCI_REG8(0xe0), 0x77}, {CCI_REG8(0xe1), 0x80}, {CCI_REG8(0xe2), 0x77}, 260 + {CCI_REG8(0xe3), 0x90}, {CCI_REG8(0xe6), 0x90}, {CCI_REG8(0xe7), 0xa0}, 261 + {CCI_REG8(0xe8), 0x90}, {CCI_REG8(0xe9), 0xa0}, 262 + /* AWB */ 263 + /* measure window */ 264 + {GC2145_REG_PAGE_SELECT, 0x00}, 265 + {CCI_REG8(0xec), 0x06}, {CCI_REG8(0xed), 0x04}, {CCI_REG8(0xee), 0x60}, 266 + {CCI_REG8(0xef), 0x90}, {CCI_REG8(0xb6), 0x01}, 267 + {GC2145_REG_PAGE_SELECT, 0x01}, 268 + {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4f), 0x00}, {CCI_REG8(0x4b), 0x01}, 269 + {CCI_REG8(0x4f), 0x00}, 270 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x71}, {CCI_REG8(0x4e), 0x01}, 271 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x91}, {CCI_REG8(0x4e), 0x01}, 272 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x70}, {CCI_REG8(0x4e), 0x01}, 273 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x90}, {CCI_REG8(0x4e), 0x02}, 274 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xb0}, {CCI_REG8(0x4e), 0x02}, 275 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8f}, {CCI_REG8(0x4e), 0x02}, 276 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6f}, {CCI_REG8(0x4e), 0x02}, 277 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaf}, {CCI_REG8(0x4e), 0x02}, 278 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xd0}, {CCI_REG8(0x4e), 0x02}, 279 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xf0}, {CCI_REG8(0x4e), 0x02}, 280 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcf}, {CCI_REG8(0x4e), 0x02}, 281 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xef}, {CCI_REG8(0x4e), 0x02}, 282 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6e}, {CCI_REG8(0x4e), 0x03}, 283 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8e}, {CCI_REG8(0x4e), 0x03}, 284 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xae}, {CCI_REG8(0x4e), 0x03}, 285 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xce}, {CCI_REG8(0x4e), 0x03}, 286 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4d}, {CCI_REG8(0x4e), 0x03}, 287 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6d}, {CCI_REG8(0x4e), 0x03}, 288 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8d}, {CCI_REG8(0x4e), 0x03}, 289 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xad}, {CCI_REG8(0x4e), 0x03}, 290 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcd}, {CCI_REG8(0x4e), 0x03}, 291 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4c}, {CCI_REG8(0x4e), 0x03}, 292 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6c}, {CCI_REG8(0x4e), 0x03}, 293 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8c}, {CCI_REG8(0x4e), 0x03}, 294 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xac}, {CCI_REG8(0x4e), 0x03}, 295 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcc}, {CCI_REG8(0x4e), 0x03}, 296 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xcb}, {CCI_REG8(0x4e), 0x03}, 297 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x4b}, {CCI_REG8(0x4e), 0x03}, 298 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x6b}, {CCI_REG8(0x4e), 0x03}, 299 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8b}, {CCI_REG8(0x4e), 0x03}, 300 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xab}, {CCI_REG8(0x4e), 0x03}, 301 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, 302 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xaa}, {CCI_REG8(0x4e), 0x04}, 303 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, 304 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x04}, 305 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x04}, 306 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x04}, 307 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x04}, 308 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x04}, 309 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0b}, {CCI_REG8(0x4e), 0x05}, 310 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x0a}, {CCI_REG8(0x4e), 0x05}, 311 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xeb}, {CCI_REG8(0x4e), 0x05}, 312 + {CCI_REG8(0x4c), 0x01}, {CCI_REG8(0x4d), 0xea}, {CCI_REG8(0x4e), 0x05}, 313 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x05}, 314 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x29}, {CCI_REG8(0x4e), 0x05}, 315 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x2a}, {CCI_REG8(0x4e), 0x05}, 316 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x4a}, {CCI_REG8(0x4e), 0x05}, 317 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x8a}, {CCI_REG8(0x4e), 0x06}, 318 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x49}, {CCI_REG8(0x4e), 0x06}, 319 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, 320 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x89}, {CCI_REG8(0x4e), 0x06}, 321 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa9}, {CCI_REG8(0x4e), 0x06}, 322 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x48}, {CCI_REG8(0x4e), 0x06}, 323 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x68}, {CCI_REG8(0x4e), 0x06}, 324 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0x69}, {CCI_REG8(0x4e), 0x06}, 325 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xca}, {CCI_REG8(0x4e), 0x07}, 326 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc9}, {CCI_REG8(0x4e), 0x07}, 327 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe9}, {CCI_REG8(0x4e), 0x07}, 328 + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x09}, {CCI_REG8(0x4e), 0x07}, 329 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc8}, {CCI_REG8(0x4e), 0x07}, 330 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe8}, {CCI_REG8(0x4e), 0x07}, 331 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xa7}, {CCI_REG8(0x4e), 0x07}, 332 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xc7}, {CCI_REG8(0x4e), 0x07}, 333 + {CCI_REG8(0x4c), 0x02}, {CCI_REG8(0x4d), 0xe7}, {CCI_REG8(0x4e), 0x07}, 334 + {CCI_REG8(0x4c), 0x03}, {CCI_REG8(0x4d), 0x07}, {CCI_REG8(0x4e), 0x07}, 335 + {CCI_REG8(0x4f), 0x01}, 336 + {CCI_REG8(0x50), 0x80}, {CCI_REG8(0x51), 0xa8}, {CCI_REG8(0x52), 0x47}, 337 + {CCI_REG8(0x53), 0x38}, {CCI_REG8(0x54), 0xc7}, {CCI_REG8(0x56), 0x0e}, 338 + {CCI_REG8(0x58), 0x08}, {CCI_REG8(0x5b), 0x00}, {CCI_REG8(0x5c), 0x74}, 339 + {CCI_REG8(0x5d), 0x8b}, {CCI_REG8(0x61), 0xdb}, {CCI_REG8(0x62), 0xb8}, 340 + {CCI_REG8(0x63), 0x86}, {CCI_REG8(0x64), 0xc0}, {CCI_REG8(0x65), 0x04}, 341 + {CCI_REG8(0x67), 0xa8}, {CCI_REG8(0x68), 0xb0}, {CCI_REG8(0x69), 0x00}, 342 + {CCI_REG8(0x6a), 0xa8}, {CCI_REG8(0x6b), 0xb0}, {CCI_REG8(0x6c), 0xaf}, 343 + {CCI_REG8(0x6d), 0x8b}, {CCI_REG8(0x6e), 0x50}, {CCI_REG8(0x6f), 0x18}, 344 + {CCI_REG8(0x73), 0xf0}, {CCI_REG8(0x70), 0x0d}, {CCI_REG8(0x71), 0x60}, 345 + {CCI_REG8(0x72), 0x80}, {CCI_REG8(0x74), 0x01}, {CCI_REG8(0x75), 0x01}, 346 + {CCI_REG8(0x7f), 0x0c}, {CCI_REG8(0x76), 0x70}, {CCI_REG8(0x77), 0x58}, 347 + {CCI_REG8(0x78), 0xa0}, {CCI_REG8(0x79), 0x5e}, {CCI_REG8(0x7a), 0x54}, 348 + {CCI_REG8(0x7b), 0x58}, 349 + /* CC */ 350 + {GC2145_REG_PAGE_SELECT, 0x02}, 351 + {CCI_REG8(0xc0), 0x01}, {CCI_REG8(0xc1), 0x44}, {CCI_REG8(0xc2), 0xfd}, 352 + {CCI_REG8(0xc3), 0x04}, {CCI_REG8(0xc4), 0xf0}, {CCI_REG8(0xc5), 0x48}, 353 + {CCI_REG8(0xc6), 0xfd}, {CCI_REG8(0xc7), 0x46}, {CCI_REG8(0xc8), 0xfd}, 354 + {CCI_REG8(0xc9), 0x02}, {CCI_REG8(0xca), 0xe0}, {CCI_REG8(0xcb), 0x45}, 355 + {CCI_REG8(0xcc), 0xec}, {CCI_REG8(0xcd), 0x48}, {CCI_REG8(0xce), 0xf0}, 356 + {CCI_REG8(0xcf), 0xf0}, {CCI_REG8(0xe3), 0x0c}, {CCI_REG8(0xe4), 0x4b}, 357 + {CCI_REG8(0xe5), 0xe0}, 358 + /* ABS */ 359 + {GC2145_REG_PAGE_SELECT, 0x01}, 360 + {CCI_REG8(0x9f), 0x40}, 361 + /* Dark sun */ 362 + {GC2145_REG_PAGE_SELECT, 0x02}, 363 + {CCI_REG8(0x40), 0xbf}, {CCI_REG8(0x46), 0xcf}, 364 + }; 365 + 366 + #define GC2145_640_480_PIXELRATE 30000000 367 + #define GC2145_640_480_LINKFREQ 120000000 368 + #define GC2145_640_480_HBLANK 0x0130 369 + #define GC2145_640_480_VBLANK 0x000c 370 + static const struct cci_reg_sequence gc2145_mode_640_480_regs[] = { 371 + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, 372 + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, 373 + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x86}, 374 + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, 375 + /* Disable PAD IO */ 376 + {GC2145_REG_PAD_IO, 0x00}, 377 + {GC2145_REG_PAGE_SELECT, 0x00}, 378 + /* Row/Col start - 0/0 */ 379 + {GC2145_REG_ROW_START, 0x0000}, 380 + {GC2145_REG_COL_START, 0x0000}, 381 + /* Window size 1216/1618 */ 382 + {GC2145_REG_WIN_HEIGHT, 0x04c0}, 383 + {GC2145_REG_WIN_WIDTH, 0x0652}, 384 + /* Scalar more */ 385 + {CCI_REG8(0xfd), 0x01}, {CCI_REG8(0xfa), 0x00}, 386 + /* Crop 640-480@0-0 */ 387 + {GC2145_REG_CROP_ENABLE, 0x01}, 388 + {GC2145_REG_CROP_Y, 0x0000}, 389 + {GC2145_REG_CROP_X, 0x0000}, 390 + {GC2145_REG_CROP_HEIGHT, 0x01e0}, 391 + {GC2145_REG_CROP_WIDTH, 0x0280}, 392 + /* Subsampling configuration */ 393 + {CCI_REG8(0x99), 0x55}, {CCI_REG8(0x9a), 0x06}, {CCI_REG8(0x9b), 0x01}, 394 + {CCI_REG8(0x9c), 0x23}, {CCI_REG8(0x9d), 0x00}, {CCI_REG8(0x9e), 0x00}, 395 + {CCI_REG8(0x9f), 0x01}, {CCI_REG8(0xa0), 0x23}, {CCI_REG8(0xa1), 0x00}, 396 + {CCI_REG8(0xa2), 0x00}, 397 + {GC2145_REG_PAGE_SELECT, 0x01}, 398 + /* AEC anti-flicker */ 399 + {CCI_REG16(0x25), 0x0175}, 400 + /* AEC exposure level 1-5 */ 401 + {CCI_REG16(0x27), 0x045f}, {CCI_REG16(0x29), 0x045f}, 402 + {CCI_REG16(0x2b), 0x045f}, {CCI_REG16(0x2d), 0x045f}, 403 + }; 404 + 405 + #define GC2145_1280_720_PIXELRATE 48000000 406 + #define GC2145_1280_720_LINKFREQ 192000000 407 + #define GC2145_1280_720_HBLANK 0x0156 408 + #define GC2145_1280_720_VBLANK 0x0011 409 + static const struct cci_reg_sequence gc2145_mode_1280_720_regs[] = { 410 + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, 411 + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, 412 + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x83}, 413 + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, 414 + /* Disable PAD IO */ 415 + {GC2145_REG_PAD_IO, 0x00}, 416 + {GC2145_REG_PAGE_SELECT, 0x00}, 417 + /* Row/Col start - 240/160 */ 418 + {GC2145_REG_ROW_START, 0x00f0}, 419 + {GC2145_REG_COL_START, 0x00a0}, 420 + /* Window size 736/1296 */ 421 + {GC2145_REG_WIN_HEIGHT, 0x02e0}, 422 + {GC2145_REG_WIN_WIDTH, 0x0510}, 423 + /* Crop 1280-720@0-0 */ 424 + {GC2145_REG_CROP_ENABLE, 0x01}, 425 + {GC2145_REG_CROP_Y, 0x0000}, 426 + {GC2145_REG_CROP_X, 0x0000}, 427 + {GC2145_REG_CROP_HEIGHT, 0x02d0}, 428 + {GC2145_REG_CROP_WIDTH, 0x0500}, 429 + {GC2145_REG_PAGE_SELECT, 0x01}, 430 + /* AEC anti-flicker */ 431 + {CCI_REG16(0x25), 0x00e6}, 432 + /* AEC exposure level 1-5 */ 433 + {CCI_REG16(0x27), 0x02b2}, {CCI_REG16(0x29), 0x02b2}, 434 + {CCI_REG16(0x2b), 0x02b2}, {CCI_REG16(0x2d), 0x02b2}, 435 + }; 436 + 437 + #define GC2145_1600_1200_PIXELRATE 60000000 438 + #define GC2145_1600_1200_LINKFREQ 240000000 439 + #define GC2145_1600_1200_HBLANK 0x0156 440 + #define GC2145_1600_1200_VBLANK 0x0010 441 + static const struct cci_reg_sequence gc2145_mode_1600_1200_regs[] = { 442 + {GC2145_REG_PAGE_SELECT, 0xf0}, {GC2145_REG_PAGE_SELECT, 0xf0}, 443 + {GC2145_REG_PAGE_SELECT, 0xf0}, {CCI_REG8(0xfc), 0x06}, 444 + {CCI_REG8(0xf6), 0x00}, {CCI_REG8(0xf7), 0x1d}, {CCI_REG8(0xf8), 0x84}, 445 + {CCI_REG8(0xfa), 0x00}, {CCI_REG8(0xf9), 0x8e}, 446 + /* Disable PAD IO */ 447 + {GC2145_REG_PAD_IO, 0x00}, 448 + {GC2145_REG_PAGE_SELECT, 0x00}, 449 + /* Row/Col start - 0/0 */ 450 + {GC2145_REG_ROW_START, 0x0000}, 451 + {GC2145_REG_COL_START, 0x0000}, 452 + /* Window size: 1216/1618 */ 453 + {GC2145_REG_WIN_HEIGHT, 0x04c0}, 454 + {GC2145_REG_WIN_WIDTH, 0x0652}, 455 + /* Crop 1600-1200@0-0 */ 456 + {GC2145_REG_CROP_ENABLE, 0x01}, 457 + {GC2145_REG_CROP_Y, 0x0000}, 458 + {GC2145_REG_CROP_X, 0x0000}, 459 + {GC2145_REG_CROP_HEIGHT, 0x04b0}, 460 + {GC2145_REG_CROP_WIDTH, 0x0640}, 461 + {GC2145_REG_PAGE_SELECT, 0x01}, 462 + /* AEC anti-flicker */ 463 + {CCI_REG16(0x25), 0x00fa}, 464 + /* AEC exposure level 1-5 */ 465 + {CCI_REG16(0x27), 0x04e2}, {CCI_REG16(0x29), 0x04e2}, 466 + {CCI_REG16(0x2b), 0x04e2}, {CCI_REG16(0x2d), 0x04e2}, 467 + }; 468 + 469 + static const s64 gc2145_link_freq_menu[] = { 470 + GC2145_640_480_LINKFREQ, 471 + GC2145_1280_720_LINKFREQ, 472 + GC2145_1600_1200_LINKFREQ, 473 + }; 474 + 475 + /* Regulators supplies */ 476 + static const char * const gc2145_supply_name[] = { 477 + "iovdd", /* Digital I/O (1.7-3V) suppply */ 478 + "avdd", /* Analog (2.7-3V) supply */ 479 + "dvdd", /* Digital Core (1.7-1.9V) supply */ 480 + }; 481 + 482 + #define GC2145_NUM_SUPPLIES ARRAY_SIZE(gc2145_supply_name) 483 + 484 + /* Mode configs */ 485 + #define GC2145_MODE_640X480 0 486 + #define GC2145_MODE_1280X720 1 487 + #define GC2145_MODE_1600X1200 2 488 + static const struct gc2145_mode supported_modes[] = { 489 + { 490 + /* 640x480 30fps mode */ 491 + .width = 640, 492 + .height = 480, 493 + .reg_seq = gc2145_mode_640_480_regs, 494 + .reg_seq_size = ARRAY_SIZE(gc2145_mode_640_480_regs), 495 + .pixel_rate = GC2145_640_480_PIXELRATE, 496 + .crop = { 497 + .top = 0, 498 + .left = 0, 499 + .width = 640, 500 + .height = 480, 501 + }, 502 + .hblank = GC2145_640_480_HBLANK, 503 + .vblank = GC2145_640_480_VBLANK, 504 + .link_freq_index = GC2145_MODE_640X480, 505 + }, 506 + { 507 + /* 1280x720 30fps mode */ 508 + .width = 1280, 509 + .height = 720, 510 + .reg_seq = gc2145_mode_1280_720_regs, 511 + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1280_720_regs), 512 + .pixel_rate = GC2145_1280_720_PIXELRATE, 513 + .crop = { 514 + .top = 160, 515 + .left = 240, 516 + .width = 1280, 517 + .height = 720, 518 + }, 519 + .hblank = GC2145_1280_720_HBLANK, 520 + .vblank = GC2145_1280_720_VBLANK, 521 + .link_freq_index = GC2145_MODE_1280X720, 522 + }, 523 + { 524 + /* 1600x1200 20fps mode */ 525 + .width = 1600, 526 + .height = 1200, 527 + .reg_seq = gc2145_mode_1600_1200_regs, 528 + .reg_seq_size = ARRAY_SIZE(gc2145_mode_1600_1200_regs), 529 + .pixel_rate = GC2145_1600_1200_PIXELRATE, 530 + .crop = { 531 + .top = 0, 532 + .left = 0, 533 + .width = 1600, 534 + .height = 1200, 535 + }, 536 + .hblank = GC2145_1600_1200_HBLANK, 537 + .vblank = GC2145_1600_1200_VBLANK, 538 + .link_freq_index = GC2145_MODE_1600X1200, 539 + }, 540 + }; 541 + 542 + /** 543 + * struct gc2145_format - GC2145 pixel format description 544 + * @code: media bus (MBUS) associated code 545 + * @datatype: MIPI CSI2 data type 546 + * @output_fmt: GC2145 output format 547 + * @switch_bit: GC2145 first/second switch 548 + */ 549 + struct gc2145_format { 550 + unsigned int code; 551 + unsigned char datatype; 552 + unsigned char output_fmt; 553 + bool switch_bit; 554 + }; 555 + 556 + /* All supported formats */ 557 + static const struct gc2145_format supported_formats[] = { 558 + { 559 + .code = MEDIA_BUS_FMT_UYVY8_1X16, 560 + .datatype = MIPI_CSI2_DT_YUV422_8B, 561 + .output_fmt = 0x00, 562 + }, 563 + { 564 + .code = MEDIA_BUS_FMT_VYUY8_1X16, 565 + .datatype = MIPI_CSI2_DT_YUV422_8B, 566 + .output_fmt = 0x01, 567 + }, 568 + { 569 + .code = MEDIA_BUS_FMT_YUYV8_1X16, 570 + .datatype = MIPI_CSI2_DT_YUV422_8B, 571 + .output_fmt = 0x02, 572 + }, 573 + { 574 + .code = MEDIA_BUS_FMT_YVYU8_1X16, 575 + .datatype = MIPI_CSI2_DT_YUV422_8B, 576 + .output_fmt = 0x03, 577 + }, 578 + { 579 + .code = MEDIA_BUS_FMT_RGB565_1X16, 580 + .datatype = MIPI_CSI2_DT_RGB565, 581 + .output_fmt = 0x06, 582 + .switch_bit = true, 583 + }, 584 + }; 585 + 586 + struct gc2145_ctrls { 587 + struct v4l2_ctrl_handler handler; 588 + struct v4l2_ctrl *pixel_rate; 589 + struct v4l2_ctrl *link_freq; 590 + struct v4l2_ctrl *test_pattern; 591 + struct v4l2_ctrl *hflip; 592 + struct v4l2_ctrl *vflip; 593 + struct v4l2_ctrl *hblank; 594 + struct v4l2_ctrl *vblank; 595 + }; 596 + 597 + struct gc2145 { 598 + struct v4l2_subdev sd; 599 + struct media_pad pad; 600 + 601 + struct regmap *regmap; 602 + struct clk *xclk; 603 + 604 + struct gpio_desc *reset_gpio; 605 + struct gpio_desc *powerdown_gpio; 606 + struct regulator_bulk_data supplies[GC2145_NUM_SUPPLIES]; 607 + 608 + /* V4L2 controls */ 609 + struct gc2145_ctrls ctrls; 610 + 611 + /* Current mode */ 612 + const struct gc2145_mode *mode; 613 + }; 614 + 615 + static inline struct gc2145 *to_gc2145(struct v4l2_subdev *_sd) 616 + { 617 + return container_of(_sd, struct gc2145, sd); 618 + } 619 + 620 + static inline struct v4l2_subdev *gc2145_ctrl_to_sd(struct v4l2_ctrl *ctrl) 621 + { 622 + return &container_of(ctrl->handler, struct gc2145, 623 + ctrls.handler)->sd; 624 + } 625 + 626 + static const struct gc2145_format * 627 + gc2145_get_format_code(struct gc2145 *gc2145, u32 code) 628 + { 629 + unsigned int i; 630 + 631 + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { 632 + if (supported_formats[i].code == code) 633 + break; 634 + } 635 + 636 + if (i >= ARRAY_SIZE(supported_formats)) 637 + i = 0; 638 + 639 + return &supported_formats[i]; 640 + } 641 + 642 + static void gc2145_update_pad_format(struct gc2145 *gc2145, 643 + const struct gc2145_mode *mode, 644 + struct v4l2_mbus_framefmt *fmt, u32 code) 645 + { 646 + fmt->code = code; 647 + fmt->width = mode->width; 648 + fmt->height = mode->height; 649 + fmt->field = V4L2_FIELD_NONE; 650 + fmt->colorspace = V4L2_COLORSPACE_SRGB; 651 + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 652 + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; 653 + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; 654 + } 655 + 656 + static int gc2145_init_cfg(struct v4l2_subdev *sd, 657 + struct v4l2_subdev_state *state) 658 + { 659 + struct gc2145 *gc2145 = to_gc2145(sd); 660 + struct v4l2_mbus_framefmt *format; 661 + struct v4l2_rect *crop; 662 + 663 + /* Initialize pad format */ 664 + format = v4l2_subdev_state_get_format(state, 0); 665 + gc2145_update_pad_format(gc2145, &supported_modes[0], format, 666 + MEDIA_BUS_FMT_RGB565_1X16); 667 + 668 + /* Initialize crop rectangle. */ 669 + crop = v4l2_subdev_state_get_crop(state, 0); 670 + *crop = supported_modes[0].crop; 671 + 672 + return 0; 673 + } 674 + 675 + static int gc2145_get_selection(struct v4l2_subdev *sd, 676 + struct v4l2_subdev_state *sd_state, 677 + struct v4l2_subdev_selection *sel) 678 + { 679 + switch (sel->target) { 680 + case V4L2_SEL_TGT_CROP: 681 + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); 682 + return 0; 683 + 684 + case V4L2_SEL_TGT_NATIVE_SIZE: 685 + sel->r.top = 0; 686 + sel->r.left = 0; 687 + sel->r.width = GC2145_NATIVE_WIDTH; 688 + sel->r.height = GC2145_NATIVE_HEIGHT; 689 + 690 + return 0; 691 + 692 + case V4L2_SEL_TGT_CROP_DEFAULT: 693 + case V4L2_SEL_TGT_CROP_BOUNDS: 694 + sel->r.top = 0; 695 + sel->r.left = 0; 696 + sel->r.width = 1600; 697 + sel->r.height = 1200; 698 + 699 + return 0; 700 + } 701 + 702 + return -EINVAL; 703 + } 704 + 705 + static int gc2145_enum_mbus_code(struct v4l2_subdev *sd, 706 + struct v4l2_subdev_state *sd_state, 707 + struct v4l2_subdev_mbus_code_enum *code) 708 + { 709 + if (code->index >= ARRAY_SIZE(supported_formats)) 710 + return -EINVAL; 711 + 712 + code->code = supported_formats[code->index].code; 713 + return 0; 714 + } 715 + 716 + static int gc2145_enum_frame_size(struct v4l2_subdev *sd, 717 + struct v4l2_subdev_state *sd_state, 718 + struct v4l2_subdev_frame_size_enum *fse) 719 + { 720 + struct gc2145 *gc2145 = to_gc2145(sd); 721 + const struct gc2145_format *gc2145_format; 722 + u32 code; 723 + 724 + if (fse->index >= ARRAY_SIZE(supported_modes)) 725 + return -EINVAL; 726 + 727 + gc2145_format = gc2145_get_format_code(gc2145, fse->code); 728 + code = gc2145_format->code; 729 + if (fse->code != code) 730 + return -EINVAL; 731 + 732 + fse->min_width = supported_modes[fse->index].width; 733 + fse->max_width = fse->min_width; 734 + fse->min_height = supported_modes[fse->index].height; 735 + fse->max_height = fse->min_height; 736 + 737 + return 0; 738 + } 739 + 740 + static int gc2145_set_pad_format(struct v4l2_subdev *sd, 741 + struct v4l2_subdev_state *sd_state, 742 + struct v4l2_subdev_format *fmt) 743 + { 744 + struct gc2145 *gc2145 = to_gc2145(sd); 745 + const struct gc2145_mode *mode; 746 + const struct gc2145_format *gc2145_fmt; 747 + struct v4l2_mbus_framefmt *framefmt; 748 + struct gc2145_ctrls *ctrls = &gc2145->ctrls; 749 + struct v4l2_rect *crop; 750 + 751 + gc2145_fmt = gc2145_get_format_code(gc2145, fmt->format.code); 752 + mode = v4l2_find_nearest_size(supported_modes, 753 + ARRAY_SIZE(supported_modes), 754 + width, height, 755 + fmt->format.width, fmt->format.height); 756 + 757 + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); 758 + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); 759 + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 760 + gc2145->mode = mode; 761 + /* Update pixel_rate based on the mode */ 762 + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mode->pixel_rate); 763 + /* Update link_freq based on the mode */ 764 + __v4l2_ctrl_s_ctrl(ctrls->link_freq, mode->link_freq_index); 765 + /* Update hblank/vblank based on the mode */ 766 + __v4l2_ctrl_s_ctrl(ctrls->hblank, mode->hblank); 767 + __v4l2_ctrl_s_ctrl(ctrls->vblank, mode->vblank); 768 + } 769 + *framefmt = fmt->format; 770 + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); 771 + *crop = mode->crop; 772 + 773 + return 0; 774 + } 775 + 776 + static const struct cci_reg_sequence gc2145_common_mipi_regs[] = { 777 + {GC2145_REG_PAGE_SELECT, 0x03}, 778 + {GC2145_REG_DPHY_ANALOG_MODE1, GC2145_DPHY_MODE_PHY_CLK_EN | 779 + GC2145_DPHY_MODE_PHY_LANE0_EN | 780 + GC2145_DPHY_MODE_PHY_LANE1_EN | 781 + GC2145_DPHY_MODE_PHY_CLK_LANE_P2S_SEL}, 782 + {GC2145_REG_DPHY_ANALOG_MODE2, GC2145_DPHY_CLK_DIFF(2) | 783 + GC2145_DPHY_LANE0_DIFF(2)}, 784 + {GC2145_REG_DPHY_ANALOG_MODE3, GC2145_DPHY_LANE1_DIFF(0) | 785 + GC2145_DPHY_CLK_DELAY}, 786 + {GC2145_REG_FIFO_MODE, GC2145_FIFO_MODE_READ_GATE | 787 + GC2145_FIFO_MODE_MIPI_CLK_MODULE}, 788 + {GC2145_REG_DPHY_MODE, GC2145_DPHY_MODE_TRIGGER_PROG}, 789 + /* Clock & Data lanes timing */ 790 + {GC2145_REG_T_LPX, 0x10}, 791 + {GC2145_REG_T_CLK_HS_PREPARE, 0x04}, {GC2145_REG_T_CLK_ZERO, 0x10}, 792 + {GC2145_REG_T_CLK_PRE, 0x10}, {GC2145_REG_T_CLK_POST, 0x10}, 793 + {GC2145_REG_T_CLK_TRAIL, 0x05}, 794 + {GC2145_REG_T_HS_PREPARE, 0x03}, {GC2145_REG_T_HS_ZERO, 0x0a}, 795 + {GC2145_REG_T_HS_TRAIL, 0x06}, 796 + }; 797 + 798 + static int gc2145_config_mipi_mode(struct gc2145 *gc2145, 799 + const struct gc2145_format *gc2145_format) 800 + { 801 + u16 lwc, fifo_full_lvl; 802 + int ret = 0; 803 + 804 + /* Common MIPI settings */ 805 + cci_multi_reg_write(gc2145->regmap, gc2145_common_mipi_regs, 806 + ARRAY_SIZE(gc2145_common_mipi_regs), &ret); 807 + 808 + /* 809 + * Adjust the MIPI buffer settings. 810 + * For YUV/RGB, LWC = image width * 2 811 + * For RAW8, LWC = image width 812 + * For RAW10, LWC = image width * 1.25 813 + */ 814 + lwc = gc2145->mode->width * 2; 815 + cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); 816 + cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); 817 + 818 + /* 819 + * Adjust the MIPI FIFO Full Level 820 + * 640x480 RGB: 0x0190 821 + * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 822 + * 1600x1200 RAW: 0x0190 823 + */ 824 + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) 825 + fifo_full_lvl = 0x0001; 826 + else 827 + fifo_full_lvl = 0x0190; 828 + 829 + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, 830 + fifo_full_lvl >> 8, &ret); 831 + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, 832 + fifo_full_lvl & 0xff, &ret); 833 + 834 + /* 835 + * Set the FIFO gate mode / MIPI wdiv set: 836 + * 0xf1 in case of RAW mode and 0xf0 otherwise 837 + */ 838 + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); 839 + 840 + /* Set the MIPI data type */ 841 + cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, 842 + gc2145_format->datatype, &ret); 843 + 844 + /* Configure mode and enable CSI */ 845 + cci_write(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, 846 + GC2145_CSI2_MODE_RAW8 | GC2145_CSI2_MODE_DOUBLE | 847 + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, &ret); 848 + 849 + return ret; 850 + } 851 + 852 + static int gc2145_start_streaming(struct gc2145 *gc2145, 853 + struct v4l2_subdev_state *state) 854 + { 855 + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); 856 + const struct gc2145_format *gc2145_format; 857 + struct v4l2_mbus_framefmt *fmt; 858 + int ret; 859 + 860 + ret = pm_runtime_resume_and_get(&client->dev); 861 + if (ret < 0) 862 + return ret; 863 + 864 + /* Apply default values of current mode */ 865 + cci_multi_reg_write(gc2145->regmap, gc2145->mode->reg_seq, 866 + gc2145->mode->reg_seq_size, &ret); 867 + cci_multi_reg_write(gc2145->regmap, gc2145_common_regs, 868 + ARRAY_SIZE(gc2145_common_regs), &ret); 869 + if (ret) { 870 + dev_err(&client->dev, "%s failed to write regs\n", __func__); 871 + goto err_rpm_put; 872 + } 873 + 874 + fmt = v4l2_subdev_state_get_format(state, 0); 875 + gc2145_format = gc2145_get_format_code(gc2145, fmt->code); 876 + 877 + /* Set the output format */ 878 + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); 879 + 880 + cci_write(gc2145->regmap, GC2145_REG_OUTPUT_FMT, 881 + gc2145_format->output_fmt, &ret); 882 + cci_update_bits(gc2145->regmap, GC2145_REG_BYPASS_MODE, 883 + GC2145_BYPASS_MODE_SWITCH, 884 + gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH 885 + : 0, &ret); 886 + if (ret) { 887 + dev_err(&client->dev, "%s failed to write regs\n", __func__); 888 + goto err_rpm_put; 889 + } 890 + 891 + /* Apply customized values from user */ 892 + ret = __v4l2_ctrl_handler_setup(&gc2145->ctrls.handler); 893 + if (ret) { 894 + dev_err(&client->dev, "%s failed to apply ctrls\n", __func__); 895 + goto err_rpm_put; 896 + } 897 + 898 + /* Perform MIPI specific configuration */ 899 + ret = gc2145_config_mipi_mode(gc2145, gc2145_format); 900 + if (ret) { 901 + dev_err(&client->dev, "%s failed to write mipi conf\n", 902 + __func__); 903 + goto err_rpm_put; 904 + } 905 + 906 + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); 907 + 908 + return 0; 909 + 910 + err_rpm_put: 911 + pm_runtime_mark_last_busy(&client->dev); 912 + pm_runtime_put_autosuspend(&client->dev); 913 + return ret; 914 + } 915 + 916 + static void gc2145_stop_streaming(struct gc2145 *gc2145) 917 + { 918 + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); 919 + int ret = 0; 920 + 921 + /* Disable lanes & mipi streaming */ 922 + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x03, &ret); 923 + cci_update_bits(gc2145->regmap, GC2145_REG_BUF_CSI2_MODE, 924 + GC2145_CSI2_MODE_EN | GC2145_CSI2_MODE_MIPI_EN, 0, 925 + &ret); 926 + cci_write(gc2145->regmap, GC2145_REG_PAGE_SELECT, 0x00, &ret); 927 + if (ret) 928 + dev_err(&client->dev, "%s failed to write regs\n", __func__); 929 + 930 + pm_runtime_mark_last_busy(&client->dev); 931 + pm_runtime_put_autosuspend(&client->dev); 932 + } 933 + 934 + static int gc2145_set_stream(struct v4l2_subdev *sd, int enable) 935 + { 936 + struct gc2145 *gc2145 = to_gc2145(sd); 937 + struct v4l2_subdev_state *state; 938 + int ret = 0; 939 + 940 + state = v4l2_subdev_lock_and_get_active_state(sd); 941 + 942 + if (enable) 943 + ret = gc2145_start_streaming(gc2145, state); 944 + else 945 + gc2145_stop_streaming(gc2145); 946 + 947 + v4l2_subdev_unlock_state(state); 948 + 949 + return ret; 950 + } 951 + 952 + /* Power/clock management functions */ 953 + static int gc2145_power_on(struct device *dev) 954 + { 955 + struct v4l2_subdev *sd = dev_get_drvdata(dev); 956 + struct gc2145 *gc2145 = to_gc2145(sd); 957 + int ret; 958 + 959 + ret = regulator_bulk_enable(GC2145_NUM_SUPPLIES, gc2145->supplies); 960 + if (ret) { 961 + dev_err(dev, "failed to enable regulators\n"); 962 + return ret; 963 + } 964 + 965 + ret = clk_prepare_enable(gc2145->xclk); 966 + if (ret) { 967 + dev_err(dev, "failed to enable clock\n"); 968 + goto reg_off; 969 + } 970 + 971 + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 0); 972 + gpiod_set_value_cansleep(gc2145->reset_gpio, 0); 973 + 974 + /* 975 + * Datasheet doesn't mention timing between PWDN/RESETB control and 976 + * i2c access however, experimentation shows that a rather big delay is 977 + * needed. 978 + */ 979 + msleep(41); 980 + 981 + return 0; 982 + 983 + reg_off: 984 + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); 985 + 986 + return ret; 987 + } 988 + 989 + static int gc2145_power_off(struct device *dev) 990 + { 991 + struct v4l2_subdev *sd = dev_get_drvdata(dev); 992 + struct gc2145 *gc2145 = to_gc2145(sd); 993 + 994 + gpiod_set_value_cansleep(gc2145->powerdown_gpio, 1); 995 + gpiod_set_value_cansleep(gc2145->reset_gpio, 1); 996 + clk_disable_unprepare(gc2145->xclk); 997 + regulator_bulk_disable(GC2145_NUM_SUPPLIES, gc2145->supplies); 998 + 999 + return 0; 1000 + } 1001 + 1002 + static int gc2145_get_regulators(struct gc2145 *gc2145) 1003 + { 1004 + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); 1005 + unsigned int i; 1006 + 1007 + for (i = 0; i < GC2145_NUM_SUPPLIES; i++) 1008 + gc2145->supplies[i].supply = gc2145_supply_name[i]; 1009 + 1010 + return devm_regulator_bulk_get(&client->dev, GC2145_NUM_SUPPLIES, 1011 + gc2145->supplies); 1012 + } 1013 + 1014 + /* Verify chip ID */ 1015 + static int gc2145_identify_module(struct gc2145 *gc2145) 1016 + { 1017 + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); 1018 + int ret; 1019 + u64 chip_id; 1020 + 1021 + ret = cci_read(gc2145->regmap, GC2145_REG_CHIP_ID, &chip_id, NULL); 1022 + if (ret) { 1023 + dev_err(&client->dev, "failed to read chip id (%d)\n", ret); 1024 + return ret; 1025 + } 1026 + 1027 + if (chip_id != GC2145_CHIP_ID) { 1028 + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", 1029 + GC2145_CHIP_ID, chip_id); 1030 + return -EIO; 1031 + } 1032 + 1033 + return 0; 1034 + } 1035 + 1036 + static const char * const test_pattern_menu[] = { 1037 + "Disabled", 1038 + "Colored patterns", 1039 + "Uniform white", 1040 + "Uniform yellow", 1041 + "Uniform cyan", 1042 + "Uniform green", 1043 + "Uniform magenta", 1044 + "Uniform red", 1045 + "Uniform black", 1046 + }; 1047 + 1048 + #define GC2145_TEST_PATTERN_ENABLE BIT(0) 1049 + #define GC2145_TEST_PATTERN_UXGA BIT(3) 1050 + 1051 + #define GC2145_TEST_UNIFORM BIT(3) 1052 + #define GC2145_TEST_WHITE (4 << 4) 1053 + #define GC2145_TEST_YELLOW (8 << 4) 1054 + #define GC2145_TEST_CYAN (9 << 4) 1055 + #define GC2145_TEST_GREEN (6 << 4) 1056 + #define GC2145_TEST_MAGENTA (10 << 4) 1057 + #define GC2145_TEST_RED (5 << 4) 1058 + #define GC2145_TEST_BLACK (0) 1059 + 1060 + static const u8 test_pattern_val[] = { 1061 + 0, 1062 + GC2145_TEST_PATTERN_ENABLE, 1063 + GC2145_TEST_UNIFORM | GC2145_TEST_WHITE, 1064 + GC2145_TEST_UNIFORM | GC2145_TEST_YELLOW, 1065 + GC2145_TEST_UNIFORM | GC2145_TEST_CYAN, 1066 + GC2145_TEST_UNIFORM | GC2145_TEST_GREEN, 1067 + GC2145_TEST_UNIFORM | GC2145_TEST_MAGENTA, 1068 + GC2145_TEST_UNIFORM | GC2145_TEST_RED, 1069 + GC2145_TEST_UNIFORM | GC2145_TEST_BLACK, 1070 + }; 1071 + 1072 + static const struct v4l2_subdev_core_ops gc2145_core_ops = { 1073 + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 1074 + .unsubscribe_event = v4l2_event_subdev_unsubscribe, 1075 + }; 1076 + 1077 + static const struct v4l2_subdev_video_ops gc2145_video_ops = { 1078 + .s_stream = gc2145_set_stream, 1079 + }; 1080 + 1081 + static const struct v4l2_subdev_pad_ops gc2145_pad_ops = { 1082 + .init_cfg = gc2145_init_cfg, 1083 + .enum_mbus_code = gc2145_enum_mbus_code, 1084 + .get_fmt = v4l2_subdev_get_fmt, 1085 + .set_fmt = gc2145_set_pad_format, 1086 + .get_selection = gc2145_get_selection, 1087 + .enum_frame_size = gc2145_enum_frame_size, 1088 + }; 1089 + 1090 + static const struct v4l2_subdev_ops gc2145_subdev_ops = { 1091 + .core = &gc2145_core_ops, 1092 + .video = &gc2145_video_ops, 1093 + .pad = &gc2145_pad_ops, 1094 + }; 1095 + 1096 + static int gc2145_set_ctrl_test_pattern(struct gc2145 *gc2145, int value) 1097 + { 1098 + int ret = 0; 1099 + 1100 + if (!value) { 1101 + /* Disable test pattern */ 1102 + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, 0, &ret); 1103 + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, 1104 + &ret); 1105 + } 1106 + 1107 + /* Enable test pattern, colored or uniform */ 1108 + cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE2, 1109 + GC2145_TEST_PATTERN_ENABLE | GC2145_TEST_PATTERN_UXGA, &ret); 1110 + 1111 + if (!(test_pattern_val[value] & GC2145_TEST_UNIFORM)) 1112 + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 0, 1113 + &ret); 1114 + 1115 + /* Uniform */ 1116 + return cci_write(gc2145->regmap, GC2145_REG_DEBUG_MODE3, 1117 + test_pattern_val[value], &ret); 1118 + } 1119 + 1120 + static int gc2145_s_ctrl(struct v4l2_ctrl *ctrl) 1121 + { 1122 + struct v4l2_subdev *sd = gc2145_ctrl_to_sd(ctrl); 1123 + struct i2c_client *client = v4l2_get_subdevdata(sd); 1124 + struct gc2145 *gc2145 = to_gc2145(sd); 1125 + int ret; 1126 + 1127 + if (pm_runtime_get_if_in_use(&client->dev) == 0) 1128 + return 0; 1129 + 1130 + switch (ctrl->id) { 1131 + case V4L2_CID_HBLANK: 1132 + ret = cci_write(gc2145->regmap, GC2145_REG_HBLANK, ctrl->val, 1133 + NULL); 1134 + break; 1135 + case V4L2_CID_VBLANK: 1136 + ret = cci_write(gc2145->regmap, GC2145_REG_VBLANK, ctrl->val, 1137 + NULL); 1138 + break; 1139 + case V4L2_CID_TEST_PATTERN: 1140 + ret = gc2145_set_ctrl_test_pattern(gc2145, ctrl->val); 1141 + break; 1142 + case V4L2_CID_HFLIP: 1143 + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, 1144 + BIT(0), (ctrl->val ? BIT(0) : 0), NULL); 1145 + break; 1146 + case V4L2_CID_VFLIP: 1147 + ret = cci_update_bits(gc2145->regmap, GC2145_REG_ANALOG_MODE1, 1148 + BIT(1), (ctrl->val ? BIT(1) : 0), NULL); 1149 + break; 1150 + default: 1151 + ret = -EINVAL; 1152 + break; 1153 + } 1154 + 1155 + pm_runtime_mark_last_busy(&client->dev); 1156 + pm_runtime_put_autosuspend(&client->dev); 1157 + 1158 + return ret; 1159 + } 1160 + 1161 + static const struct v4l2_ctrl_ops gc2145_ctrl_ops = { 1162 + .s_ctrl = gc2145_s_ctrl, 1163 + }; 1164 + 1165 + /* Initialize control handlers */ 1166 + static int gc2145_init_controls(struct gc2145 *gc2145) 1167 + { 1168 + struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd); 1169 + const struct v4l2_ctrl_ops *ops = &gc2145_ctrl_ops; 1170 + struct gc2145_ctrls *ctrls = &gc2145->ctrls; 1171 + struct v4l2_ctrl_handler *hdl = &ctrls->handler; 1172 + struct v4l2_fwnode_device_properties props; 1173 + int ret; 1174 + 1175 + ret = v4l2_ctrl_handler_init(hdl, 12); 1176 + if (ret) 1177 + return ret; 1178 + 1179 + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, 1180 + GC2145_640_480_PIXELRATE, 1181 + GC2145_1600_1200_PIXELRATE, 1, 1182 + supported_modes[0].pixel_rate); 1183 + 1184 + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 1185 + ARRAY_SIZE(gc2145_link_freq_menu) - 1, 1186 + 0, gc2145_link_freq_menu); 1187 + if (ctrls->link_freq) 1188 + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1189 + 1190 + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 1191 + 0, 0xfff, 1, GC2145_640_480_HBLANK); 1192 + 1193 + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, 1194 + 0, 0x1fff, 1, GC2145_640_480_VBLANK); 1195 + 1196 + ctrls->test_pattern = 1197 + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, 1198 + ARRAY_SIZE(test_pattern_menu) - 1, 1199 + 0, 0, test_pattern_menu); 1200 + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 1201 + 0, 1, 1, 0); 1202 + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 1203 + 0, 1, 1, 0); 1204 + 1205 + if (hdl->error) { 1206 + ret = hdl->error; 1207 + dev_err(&client->dev, "control init failed (%d)\n", ret); 1208 + goto error; 1209 + } 1210 + 1211 + ret = v4l2_fwnode_device_parse(&client->dev, &props); 1212 + if (ret) 1213 + goto error; 1214 + 1215 + ret = v4l2_ctrl_new_fwnode_properties(hdl, &gc2145_ctrl_ops, 1216 + &props); 1217 + if (ret) 1218 + goto error; 1219 + 1220 + gc2145->sd.ctrl_handler = hdl; 1221 + 1222 + return 0; 1223 + 1224 + error: 1225 + v4l2_ctrl_handler_free(hdl); 1226 + 1227 + return ret; 1228 + } 1229 + 1230 + static int gc2145_check_hwcfg(struct device *dev) 1231 + { 1232 + struct fwnode_handle *endpoint; 1233 + struct v4l2_fwnode_endpoint ep_cfg = { 1234 + .bus_type = V4L2_MBUS_CSI2_DPHY 1235 + }; 1236 + int ret; 1237 + 1238 + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); 1239 + if (!endpoint) { 1240 + dev_err(dev, "endpoint node not found\n"); 1241 + return -EINVAL; 1242 + } 1243 + 1244 + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg); 1245 + fwnode_handle_put(endpoint); 1246 + if (ret) 1247 + return ret; 1248 + 1249 + /* Check the number of MIPI CSI2 data lanes */ 1250 + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { 1251 + dev_err(dev, "only 2 data lanes are currently supported\n"); 1252 + ret = -EINVAL; 1253 + goto out; 1254 + } 1255 + 1256 + /* Check the link frequency set in device tree */ 1257 + if (!ep_cfg.nr_of_link_frequencies) { 1258 + dev_err(dev, "link-frequency property not found in DT\n"); 1259 + ret = -EINVAL; 1260 + goto out; 1261 + } 1262 + 1263 + if (ep_cfg.nr_of_link_frequencies != 3 || 1264 + ep_cfg.link_frequencies[0] != GC2145_640_480_LINKFREQ || 1265 + ep_cfg.link_frequencies[1] != GC2145_1280_720_LINKFREQ || 1266 + ep_cfg.link_frequencies[2] != GC2145_1600_1200_LINKFREQ) { 1267 + dev_err(dev, "Invalid link-frequencies provided\n"); 1268 + ret = -EINVAL; 1269 + } 1270 + 1271 + out: 1272 + v4l2_fwnode_endpoint_free(&ep_cfg); 1273 + 1274 + return ret; 1275 + } 1276 + 1277 + static int gc2145_probe(struct i2c_client *client) 1278 + { 1279 + struct device *dev = &client->dev; 1280 + unsigned int xclk_freq; 1281 + struct gc2145 *gc2145; 1282 + int ret; 1283 + 1284 + gc2145 = devm_kzalloc(&client->dev, sizeof(*gc2145), GFP_KERNEL); 1285 + if (!gc2145) 1286 + return -ENOMEM; 1287 + 1288 + v4l2_i2c_subdev_init(&gc2145->sd, client, &gc2145_subdev_ops); 1289 + 1290 + /* Check the hardware configuration in device tree */ 1291 + if (gc2145_check_hwcfg(dev)) 1292 + return -EINVAL; 1293 + 1294 + /* Get system clock (xclk) */ 1295 + gc2145->xclk = devm_clk_get(dev, NULL); 1296 + if (IS_ERR(gc2145->xclk)) 1297 + return dev_err_probe(dev, PTR_ERR(gc2145->xclk), 1298 + "failed to get xclk\n"); 1299 + 1300 + xclk_freq = clk_get_rate(gc2145->xclk); 1301 + if (xclk_freq != GC2145_XCLK_FREQ) { 1302 + dev_err(dev, "xclk frequency not supported: %d Hz\n", 1303 + xclk_freq); 1304 + return -EINVAL; 1305 + } 1306 + 1307 + ret = gc2145_get_regulators(gc2145); 1308 + if (ret) 1309 + return dev_err_probe(dev, ret, 1310 + "failed to get regulators\n"); 1311 + 1312 + /* Request optional reset pin */ 1313 + gc2145->reset_gpio = devm_gpiod_get_optional(dev, "reset", 1314 + GPIOD_OUT_HIGH); 1315 + if (IS_ERR(gc2145->reset_gpio)) 1316 + return dev_err_probe(dev, PTR_ERR(gc2145->reset_gpio), 1317 + "failed to get reset_gpio\n"); 1318 + 1319 + /* Request optional powerdown pin */ 1320 + gc2145->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", 1321 + GPIOD_OUT_HIGH); 1322 + if (IS_ERR(gc2145->powerdown_gpio)) 1323 + return dev_err_probe(dev, PTR_ERR(gc2145->powerdown_gpio), 1324 + "failed to get powerdown_gpio\n"); 1325 + 1326 + /* Initialise the regmap for further cci access */ 1327 + gc2145->regmap = devm_cci_regmap_init_i2c(client, 8); 1328 + if (IS_ERR(gc2145->regmap)) 1329 + return dev_err_probe(dev, PTR_ERR(gc2145->regmap), 1330 + "failed to get cci regmap\n"); 1331 + 1332 + /* 1333 + * The sensor must be powered for gc2145_identify_module() 1334 + * to be able to read the CHIP_ID register 1335 + */ 1336 + ret = gc2145_power_on(dev); 1337 + if (ret) 1338 + return ret; 1339 + 1340 + ret = gc2145_identify_module(gc2145); 1341 + if (ret) 1342 + goto error_power_off; 1343 + 1344 + /* Set default mode */ 1345 + gc2145->mode = &supported_modes[0]; 1346 + 1347 + ret = gc2145_init_controls(gc2145); 1348 + if (ret) 1349 + goto error_power_off; 1350 + 1351 + /* Initialize subdev */ 1352 + gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 1353 + V4L2_SUBDEV_FL_HAS_EVENTS; 1354 + gc2145->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1355 + 1356 + /* Initialize source pad */ 1357 + gc2145->pad.flags = MEDIA_PAD_FL_SOURCE; 1358 + 1359 + ret = media_entity_pads_init(&gc2145->sd.entity, 1, &gc2145->pad); 1360 + if (ret) { 1361 + dev_err(dev, "failed to init entity pads: %d\n", ret); 1362 + goto error_handler_free; 1363 + } 1364 + 1365 + gc2145->sd.state_lock = gc2145->ctrls.handler.lock; 1366 + ret = v4l2_subdev_init_finalize(&gc2145->sd); 1367 + if (ret < 0) { 1368 + dev_err(dev, "subdev init error: %d\n", ret); 1369 + goto error_media_entity; 1370 + } 1371 + 1372 + /* Enable runtime PM and turn off the device */ 1373 + pm_runtime_set_active(dev); 1374 + pm_runtime_get_noresume(&client->dev); 1375 + pm_runtime_enable(dev); 1376 + 1377 + pm_runtime_set_autosuspend_delay(&client->dev, 1000); 1378 + pm_runtime_use_autosuspend(&client->dev); 1379 + pm_runtime_put_autosuspend(&client->dev); 1380 + 1381 + ret = v4l2_async_register_subdev_sensor(&gc2145->sd); 1382 + if (ret < 0) { 1383 + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); 1384 + goto error_subdev_cleanup; 1385 + } 1386 + 1387 + return 0; 1388 + 1389 + error_subdev_cleanup: 1390 + v4l2_subdev_cleanup(&gc2145->sd); 1391 + pm_runtime_disable(&client->dev); 1392 + pm_runtime_set_suspended(&client->dev); 1393 + 1394 + error_media_entity: 1395 + media_entity_cleanup(&gc2145->sd.entity); 1396 + 1397 + error_handler_free: 1398 + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); 1399 + 1400 + error_power_off: 1401 + gc2145_power_off(dev); 1402 + 1403 + return ret; 1404 + } 1405 + 1406 + static void gc2145_remove(struct i2c_client *client) 1407 + { 1408 + struct v4l2_subdev *sd = i2c_get_clientdata(client); 1409 + struct gc2145 *gc2145 = to_gc2145(sd); 1410 + 1411 + v4l2_subdev_cleanup(sd); 1412 + v4l2_async_unregister_subdev(sd); 1413 + media_entity_cleanup(&sd->entity); 1414 + v4l2_ctrl_handler_free(&gc2145->ctrls.handler); 1415 + 1416 + pm_runtime_disable(&client->dev); 1417 + if (!pm_runtime_status_suspended(&client->dev)) 1418 + gc2145_power_off(&client->dev); 1419 + pm_runtime_set_suspended(&client->dev); 1420 + } 1421 + 1422 + static const struct of_device_id gc2145_dt_ids[] = { 1423 + { .compatible = "galaxycore,gc2145" }, 1424 + { /* sentinel */ } 1425 + }; 1426 + MODULE_DEVICE_TABLE(of, gc2145_dt_ids); 1427 + 1428 + static const struct dev_pm_ops gc2145_pm_ops = { 1429 + RUNTIME_PM_OPS(gc2145_power_off, gc2145_power_on, NULL) 1430 + }; 1431 + 1432 + static struct i2c_driver gc2145_i2c_driver = { 1433 + .driver = { 1434 + .name = "gc2145", 1435 + .of_match_table = gc2145_dt_ids, 1436 + .pm = pm_ptr(&gc2145_pm_ops), 1437 + }, 1438 + .probe = gc2145_probe, 1439 + .remove = gc2145_remove, 1440 + }; 1441 + 1442 + module_i2c_driver(gc2145_i2c_driver); 1443 + 1444 + MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); 1445 + MODULE_DESCRIPTION("GalaxyCore GC2145 sensor driver"); 1446 + MODULE_LICENSE("GPL");