xref: /openbmc/linux/drivers/media/i2c/hi846.c (revision 04fc06f6)
1e8c08826SMartin Kepplinger // SPDX-License-Identifier: GPL-2.0
2e8c08826SMartin Kepplinger // Copyright (c) 2021 Purism SPC
3e8c08826SMartin Kepplinger 
4e8c08826SMartin Kepplinger #include <asm/unaligned.h>
5e8c08826SMartin Kepplinger #include <linux/clk.h>
6e8c08826SMartin Kepplinger #include <linux/delay.h>
7e8c08826SMartin Kepplinger #include <linux/gpio/consumer.h>
8e8c08826SMartin Kepplinger #include <linux/i2c.h>
9e8c08826SMartin Kepplinger #include <linux/module.h>
10e8c08826SMartin Kepplinger #include <linux/pm_runtime.h>
11e8c08826SMartin Kepplinger #include <linux/pm.h>
12e7cc3e09SMartin Kepplinger #include <linux/property.h>
13e8c08826SMartin Kepplinger #include <linux/regulator/consumer.h>
14e8c08826SMartin Kepplinger #include <media/v4l2-ctrls.h>
15e8c08826SMartin Kepplinger #include <media/v4l2-device.h>
16e8c08826SMartin Kepplinger #include <media/v4l2-fwnode.h>
17e8c08826SMartin Kepplinger 
18e8c08826SMartin Kepplinger #define HI846_MEDIA_BUS_FORMAT		MEDIA_BUS_FMT_SGBRG10_1X10
19e8c08826SMartin Kepplinger #define HI846_RGB_DEPTH			10
20e8c08826SMartin Kepplinger 
21e8c08826SMartin Kepplinger /* Frame length lines / vertical timings */
22e8c08826SMartin Kepplinger #define HI846_REG_FLL			0x0006
23e8c08826SMartin Kepplinger #define HI846_FLL_MAX			0xffff
24e8c08826SMartin Kepplinger 
25e8c08826SMartin Kepplinger /* Horizontal timing */
26e8c08826SMartin Kepplinger #define HI846_REG_LLP			0x0008
27e8c08826SMartin Kepplinger #define HI846_LINE_LENGTH		3800
28e8c08826SMartin Kepplinger 
29e8c08826SMartin Kepplinger #define HI846_REG_BINNING_MODE		0x000c
30e8c08826SMartin Kepplinger 
31e8c08826SMartin Kepplinger #define HI846_REG_IMAGE_ORIENTATION	0x000e
32e8c08826SMartin Kepplinger 
33e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0022		0x0022
34e8c08826SMartin Kepplinger 
35e8c08826SMartin Kepplinger #define HI846_REG_Y_ADDR_START_VACT_H	0x0026
36e8c08826SMartin Kepplinger #define HI846_REG_Y_ADDR_START_VACT_L	0x0027
37e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0028		0x0028
38e8c08826SMartin Kepplinger 
39e8c08826SMartin Kepplinger #define HI846_REG_Y_ADDR_END_VACT_H	0x002c
40e8c08826SMartin Kepplinger #define HI846_REG_Y_ADDR_END_VACT_L	0x002d
41e8c08826SMartin Kepplinger 
42e8c08826SMartin Kepplinger #define HI846_REG_Y_ODD_INC_FOBP	0x002e
43e8c08826SMartin Kepplinger #define HI846_REG_Y_EVEN_INC_FOBP	0x002f
44e8c08826SMartin Kepplinger 
45e8c08826SMartin Kepplinger #define HI846_REG_Y_ODD_INC_VACT	0x0032
46e8c08826SMartin Kepplinger #define HI846_REG_Y_EVEN_INC_VACT	0x0033
47e8c08826SMartin Kepplinger 
48e8c08826SMartin Kepplinger #define HI846_REG_GROUPED_PARA_HOLD	0x0046
49e8c08826SMartin Kepplinger 
50e8c08826SMartin Kepplinger #define HI846_REG_TG_ENABLE		0x004c
51e8c08826SMartin Kepplinger 
52e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_005C		0x005c
53e8c08826SMartin Kepplinger 
54e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_006A		0x006a
55e8c08826SMartin Kepplinger 
56e8c08826SMartin Kepplinger /*
57e8c08826SMartin Kepplinger  * Long exposure time. Actually, exposure is a 20 bit value that
58e8c08826SMartin Kepplinger  * includes the lower 4 bits of 0x0073 too. Only 16 bits are used
59e8c08826SMartin Kepplinger  * right now
60e8c08826SMartin Kepplinger  */
61e8c08826SMartin Kepplinger #define HI846_REG_EXPOSURE		0x0074
62e8c08826SMartin Kepplinger #define HI846_EXPOSURE_MIN		6
63e8c08826SMartin Kepplinger #define HI846_EXPOSURE_MAX_MARGIN	2
64e8c08826SMartin Kepplinger #define HI846_EXPOSURE_STEP		1
65e8c08826SMartin Kepplinger 
66e8c08826SMartin Kepplinger /* Analog gain controls from sensor */
67e8c08826SMartin Kepplinger #define HI846_REG_ANALOG_GAIN		0x0077
68e8c08826SMartin Kepplinger #define HI846_ANAL_GAIN_MIN		0
69e8c08826SMartin Kepplinger #define HI846_ANAL_GAIN_MAX		240
70e8c08826SMartin Kepplinger #define HI846_ANAL_GAIN_STEP		8
71e8c08826SMartin Kepplinger 
72e8c08826SMartin Kepplinger /* Digital gain controls from sensor */
73e8c08826SMartin Kepplinger #define HI846_REG_MWB_GR_GAIN_H		0x0078
74e8c08826SMartin Kepplinger #define HI846_REG_MWB_GR_GAIN_L		0x0079
75e8c08826SMartin Kepplinger #define HI846_REG_MWB_GB_GAIN_H		0x007a
76e8c08826SMartin Kepplinger #define HI846_REG_MWB_GB_GAIN_L		0x007b
77e8c08826SMartin Kepplinger #define HI846_REG_MWB_R_GAIN_H		0x007c
78e8c08826SMartin Kepplinger #define HI846_REG_MWB_R_GAIN_L		0x007d
79e8c08826SMartin Kepplinger #define HI846_REG_MWB_B_GAIN_H		0x007e
80e8c08826SMartin Kepplinger #define HI846_REG_MWB_B_GAIN_L		0x007f
81e8c08826SMartin Kepplinger #define HI846_DGTL_GAIN_MIN		512
82e8c08826SMartin Kepplinger #define HI846_DGTL_GAIN_MAX		8191
83e8c08826SMartin Kepplinger #define HI846_DGTL_GAIN_STEP		1
84e8c08826SMartin Kepplinger #define HI846_DGTL_GAIN_DEFAULT		512
85e8c08826SMartin Kepplinger 
86e8c08826SMartin Kepplinger #define HI846_REG_X_ADDR_START_HACT_H	0x0120
87e8c08826SMartin Kepplinger #define HI846_REG_X_ADDR_END_HACT_H	0x0122
88e8c08826SMartin Kepplinger 
89e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_012A		0x012a
90e8c08826SMartin Kepplinger 
91e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0200		0x0200
92e8c08826SMartin Kepplinger 
93e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_021C		0x021c
94e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_021E		0x021e
95e8c08826SMartin Kepplinger 
96e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0402		0x0402
97e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0404		0x0404
98e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0408		0x0408
99e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0410		0x0410
100e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0412		0x0412
101e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0414		0x0414
102e8c08826SMartin Kepplinger 
103e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0418		0x0418
104e8c08826SMartin Kepplinger 
105e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_051E		0x051e
106e8c08826SMartin Kepplinger 
107e8c08826SMartin Kepplinger /* Formatter */
108e8c08826SMartin Kepplinger #define HI846_REG_X_START_H		0x0804
109e8c08826SMartin Kepplinger #define HI846_REG_X_START_L		0x0805
110e8c08826SMartin Kepplinger 
111e8c08826SMartin Kepplinger /* MIPI */
112e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0900		0x0900
113e8c08826SMartin Kepplinger #define HI846_REG_MIPI_TX_OP_EN		0x0901
114e8c08826SMartin Kepplinger #define HI846_REG_MIPI_TX_OP_MODE	0x0902
115e8c08826SMartin Kepplinger #define HI846_RAW8			BIT(5)
116e8c08826SMartin Kepplinger 
117e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_090C		0x090c
118e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_090E		0x090e
119e8c08826SMartin Kepplinger 
120e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0914		0x0914
121e8c08826SMartin Kepplinger #define HI846_REG_TLPX			0x0915
122e8c08826SMartin Kepplinger #define HI846_REG_TCLK_PREPARE		0x0916
123e8c08826SMartin Kepplinger #define HI846_REG_TCLK_ZERO		0x0917
124e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0918		0x0918
125e8c08826SMartin Kepplinger #define HI846_REG_THS_PREPARE		0x0919
126e8c08826SMartin Kepplinger #define HI846_REG_THS_ZERO		0x091a
127e8c08826SMartin Kepplinger #define HI846_REG_THS_TRAIL		0x091b
128e8c08826SMartin Kepplinger #define HI846_REG_TCLK_POST		0x091c
129e8c08826SMartin Kepplinger #define HI846_REG_TCLK_TRAIL_MIN	0x091d
130e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_091E		0x091e
131e8c08826SMartin Kepplinger 
132e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0954		0x0954
133e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0956		0x0956
134e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0958		0x0958
135e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_095A		0x095a
136e8c08826SMartin Kepplinger 
137e8c08826SMartin Kepplinger /* ISP Common */
138e8c08826SMartin Kepplinger #define HI846_REG_MODE_SELECT		0x0a00
139e8c08826SMartin Kepplinger #define HI846_MODE_STANDBY		0x00
140e8c08826SMartin Kepplinger #define HI846_MODE_STREAMING		0x01
141e8c08826SMartin Kepplinger #define HI846_REG_FAST_STANDBY_MODE	0x0a02
142e8c08826SMartin Kepplinger #define HI846_REG_ISP_EN_H		0x0a04
143e8c08826SMartin Kepplinger 
144e8c08826SMartin Kepplinger /* Test Pattern Control */
145e8c08826SMartin Kepplinger #define HI846_REG_ISP			0x0a05
146e8c08826SMartin Kepplinger #define HI846_REG_ISP_TPG_EN		0x01
147e8c08826SMartin Kepplinger #define HI846_REG_TEST_PATTERN		0x020a /* 1-9 */
148e8c08826SMartin Kepplinger 
149e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0A0C		0x0a0c
150e8c08826SMartin Kepplinger 
151e8c08826SMartin Kepplinger /* Windowing */
152e8c08826SMartin Kepplinger #define HI846_REG_X_OUTPUT_SIZE_H	0x0a12
153e8c08826SMartin Kepplinger #define HI846_REG_X_OUTPUT_SIZE_L	0x0a13
154e8c08826SMartin Kepplinger #define HI846_REG_Y_OUTPUT_SIZE_H	0x0a14
155e8c08826SMartin Kepplinger #define HI846_REG_Y_OUTPUT_SIZE_L	0x0a15
156e8c08826SMartin Kepplinger 
157e8c08826SMartin Kepplinger /* ISP Common */
158e8c08826SMartin Kepplinger #define HI846_REG_PEDESTAL_EN		0x0a1a
159e8c08826SMartin Kepplinger 
160e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0A1E		0x0a1e
161e8c08826SMartin Kepplinger 
162e8c08826SMartin Kepplinger /* Horizontal Binning Mode */
163e8c08826SMartin Kepplinger #define HI846_REG_HBIN_MODE		0x0a22
164e8c08826SMartin Kepplinger 
165e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0A24		0x0a24
166e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0B02		0x0b02
167e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0B10		0x0b10
168e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0B12		0x0b12
169e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0B14		0x0b14
170e8c08826SMartin Kepplinger 
171e8c08826SMartin Kepplinger /* BLC (Black Level Calibration) */
172e8c08826SMartin Kepplinger #define HI846_REG_BLC_CTL0		0x0c00
173e8c08826SMartin Kepplinger 
174e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0C06		0x0c06
175e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0C10		0x0c10
176e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0C12		0x0c12
177e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0C14		0x0c14
178e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0C16		0x0c16
179e8c08826SMartin Kepplinger 
180e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0E04		0x0e04
181e8c08826SMartin Kepplinger 
182e8c08826SMartin Kepplinger #define HI846_REG_CHIP_ID_L		0x0f16
183e8c08826SMartin Kepplinger #define HI846_REG_CHIP_ID_H		0x0f17
184e8c08826SMartin Kepplinger #define HI846_CHIP_ID_L			0x46
185e8c08826SMartin Kepplinger #define HI846_CHIP_ID_H			0x08
186e8c08826SMartin Kepplinger 
187e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0F04		0x0f04
188e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0F08		0x0f08
189e8c08826SMartin Kepplinger 
190e8c08826SMartin Kepplinger /* PLL */
191e8c08826SMartin Kepplinger #define HI846_REG_PLL_CFG_MIPI2_H	0x0f2a
192e8c08826SMartin Kepplinger #define HI846_REG_PLL_CFG_MIPI2_L	0x0f2b
193e8c08826SMartin Kepplinger 
194e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0F30		0x0f30
195e8c08826SMartin Kepplinger #define HI846_REG_PLL_CFG_RAMP1_H	0x0f32
196e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_0F36		0x0f36
197e8c08826SMartin Kepplinger #define HI846_REG_PLL_CFG_MIPI1_H	0x0f38
198e8c08826SMartin Kepplinger 
199e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_2008		0x2008
200e8c08826SMartin Kepplinger #define HI846_REG_UNKNOWN_326E		0x326e
201e8c08826SMartin Kepplinger 
202e8c08826SMartin Kepplinger struct hi846_reg {
203e8c08826SMartin Kepplinger 	u16 address;
204e8c08826SMartin Kepplinger 	u16 val;
205e8c08826SMartin Kepplinger };
206e8c08826SMartin Kepplinger 
207e8c08826SMartin Kepplinger struct hi846_reg_list {
208e8c08826SMartin Kepplinger 	u32 num_of_regs;
209e8c08826SMartin Kepplinger 	const struct hi846_reg *regs;
210e8c08826SMartin Kepplinger };
211e8c08826SMartin Kepplinger 
212e8c08826SMartin Kepplinger struct hi846_mode {
213e8c08826SMartin Kepplinger 	/* Frame width in pixels */
214e8c08826SMartin Kepplinger 	u32 width;
215e8c08826SMartin Kepplinger 
216e8c08826SMartin Kepplinger 	/* Frame height in pixels */
217e8c08826SMartin Kepplinger 	u32 height;
218e8c08826SMartin Kepplinger 
219e8c08826SMartin Kepplinger 	/* Horizontal timing size */
220e8c08826SMartin Kepplinger 	u32 llp;
221e8c08826SMartin Kepplinger 
222e8c08826SMartin Kepplinger 	/* Link frequency needed for this resolution */
223e8c08826SMartin Kepplinger 	u8 link_freq_index;
224e8c08826SMartin Kepplinger 
225e8c08826SMartin Kepplinger 	u16 fps;
226e8c08826SMartin Kepplinger 
227e8c08826SMartin Kepplinger 	/* Vertical timining size */
228e8c08826SMartin Kepplinger 	u16 frame_len;
229e8c08826SMartin Kepplinger 
230e8c08826SMartin Kepplinger 	const struct hi846_reg_list reg_list_config;
231e8c08826SMartin Kepplinger 	const struct hi846_reg_list reg_list_2lane;
232e8c08826SMartin Kepplinger 	const struct hi846_reg_list reg_list_4lane;
233e8c08826SMartin Kepplinger 
234e8c08826SMartin Kepplinger 	/* Position inside of the 3264x2448 pixel array */
235e8c08826SMartin Kepplinger 	struct v4l2_rect crop;
236e8c08826SMartin Kepplinger };
237e8c08826SMartin Kepplinger 
238e8c08826SMartin Kepplinger static const struct hi846_reg hi846_init_2lane[] = {
239e8c08826SMartin Kepplinger 	{HI846_REG_MODE_SELECT,		0x0000},
240e8c08826SMartin Kepplinger 	/* regs below are unknown */
241e8c08826SMartin Kepplinger 	{0x2000, 0x100a},
242e8c08826SMartin Kepplinger 	{0x2002, 0x00ff},
243e8c08826SMartin Kepplinger 	{0x2004, 0x0007},
244e8c08826SMartin Kepplinger 	{0x2006, 0x3fff},
245e8c08826SMartin Kepplinger 	{0x2008, 0x3fff},
246e8c08826SMartin Kepplinger 	{0x200a, 0xc216},
247e8c08826SMartin Kepplinger 	{0x200c, 0x1292},
248e8c08826SMartin Kepplinger 	{0x200e, 0xc01a},
249e8c08826SMartin Kepplinger 	{0x2010, 0x403d},
250e8c08826SMartin Kepplinger 	{0x2012, 0x000e},
251e8c08826SMartin Kepplinger 	{0x2014, 0x403e},
252e8c08826SMartin Kepplinger 	{0x2016, 0x0b80},
253e8c08826SMartin Kepplinger 	{0x2018, 0x403f},
254e8c08826SMartin Kepplinger 	{0x201a, 0x82ae},
255e8c08826SMartin Kepplinger 	{0x201c, 0x1292},
256e8c08826SMartin Kepplinger 	{0x201e, 0xc00c},
257e8c08826SMartin Kepplinger 	{0x2020, 0x4130},
258e8c08826SMartin Kepplinger 	{0x2022, 0x43e2},
259e8c08826SMartin Kepplinger 	{0x2024, 0x0180},
260e8c08826SMartin Kepplinger 	{0x2026, 0x4130},
261e8c08826SMartin Kepplinger 	{0x2028, 0x7400},
262e8c08826SMartin Kepplinger 	{0x202a, 0x5000},
263e8c08826SMartin Kepplinger 	{0x202c, 0x0253},
264e8c08826SMartin Kepplinger 	{0x202e, 0x0ad1},
265e8c08826SMartin Kepplinger 	{0x2030, 0x2360},
266e8c08826SMartin Kepplinger 	{0x2032, 0x0009},
267e8c08826SMartin Kepplinger 	{0x2034, 0x5020},
268e8c08826SMartin Kepplinger 	{0x2036, 0x000b},
269e8c08826SMartin Kepplinger 	{0x2038, 0x0002},
270e8c08826SMartin Kepplinger 	{0x203a, 0x0044},
271e8c08826SMartin Kepplinger 	{0x203c, 0x0016},
272e8c08826SMartin Kepplinger 	{0x203e, 0x1792},
273e8c08826SMartin Kepplinger 	{0x2040, 0x7002},
274e8c08826SMartin Kepplinger 	{0x2042, 0x154f},
275e8c08826SMartin Kepplinger 	{0x2044, 0x00d5},
276e8c08826SMartin Kepplinger 	{0x2046, 0x000b},
277e8c08826SMartin Kepplinger 	{0x2048, 0x0019},
278e8c08826SMartin Kepplinger 	{0x204a, 0x1698},
279e8c08826SMartin Kepplinger 	{0x204c, 0x000e},
280e8c08826SMartin Kepplinger 	{0x204e, 0x099a},
281e8c08826SMartin Kepplinger 	{0x2050, 0x0058},
282e8c08826SMartin Kepplinger 	{0x2052, 0x7000},
283e8c08826SMartin Kepplinger 	{0x2054, 0x1799},
284e8c08826SMartin Kepplinger 	{0x2056, 0x0310},
285e8c08826SMartin Kepplinger 	{0x2058, 0x03c3},
286e8c08826SMartin Kepplinger 	{0x205a, 0x004c},
287e8c08826SMartin Kepplinger 	{0x205c, 0x064a},
288e8c08826SMartin Kepplinger 	{0x205e, 0x0001},
289e8c08826SMartin Kepplinger 	{0x2060, 0x0007},
290e8c08826SMartin Kepplinger 	{0x2062, 0x0bc7},
291e8c08826SMartin Kepplinger 	{0x2064, 0x0055},
292e8c08826SMartin Kepplinger 	{0x2066, 0x7000},
293e8c08826SMartin Kepplinger 	{0x2068, 0x1550},
294e8c08826SMartin Kepplinger 	{0x206a, 0x158a},
295e8c08826SMartin Kepplinger 	{0x206c, 0x0004},
296e8c08826SMartin Kepplinger 	{0x206e, 0x1488},
297e8c08826SMartin Kepplinger 	{0x2070, 0x7010},
298e8c08826SMartin Kepplinger 	{0x2072, 0x1508},
299e8c08826SMartin Kepplinger 	{0x2074, 0x0004},
300e8c08826SMartin Kepplinger 	{0x2076, 0x0016},
301e8c08826SMartin Kepplinger 	{0x2078, 0x03d5},
302e8c08826SMartin Kepplinger 	{0x207a, 0x0055},
303e8c08826SMartin Kepplinger 	{0x207c, 0x08ca},
304e8c08826SMartin Kepplinger 	{0x207e, 0x2019},
305e8c08826SMartin Kepplinger 	{0x2080, 0x0007},
306e8c08826SMartin Kepplinger 	{0x2082, 0x7057},
307e8c08826SMartin Kepplinger 	{0x2084, 0x0fc7},
308e8c08826SMartin Kepplinger 	{0x2086, 0x5041},
309e8c08826SMartin Kepplinger 	{0x2088, 0x12c8},
310e8c08826SMartin Kepplinger 	{0x208a, 0x5060},
311e8c08826SMartin Kepplinger 	{0x208c, 0x5080},
312e8c08826SMartin Kepplinger 	{0x208e, 0x2084},
313e8c08826SMartin Kepplinger 	{0x2090, 0x12c8},
314e8c08826SMartin Kepplinger 	{0x2092, 0x7800},
315e8c08826SMartin Kepplinger 	{0x2094, 0x0802},
316e8c08826SMartin Kepplinger 	{0x2096, 0x040f},
317e8c08826SMartin Kepplinger 	{0x2098, 0x1007},
318e8c08826SMartin Kepplinger 	{0x209a, 0x0803},
319e8c08826SMartin Kepplinger 	{0x209c, 0x080b},
320e8c08826SMartin Kepplinger 	{0x209e, 0x3803},
321e8c08826SMartin Kepplinger 	{0x20a0, 0x0807},
322e8c08826SMartin Kepplinger 	{0x20a2, 0x0404},
323e8c08826SMartin Kepplinger 	{0x20a4, 0x0400},
324e8c08826SMartin Kepplinger 	{0x20a6, 0xffff},
325e8c08826SMartin Kepplinger 	{0x20a8, 0xf0b2},
326e8c08826SMartin Kepplinger 	{0x20aa, 0xffef},
327e8c08826SMartin Kepplinger 	{0x20ac, 0x0a84},
328e8c08826SMartin Kepplinger 	{0x20ae, 0x1292},
329e8c08826SMartin Kepplinger 	{0x20b0, 0xc02e},
330e8c08826SMartin Kepplinger 	{0x20b2, 0x4130},
331e8c08826SMartin Kepplinger 	{0x23fe, 0xc056},
332e8c08826SMartin Kepplinger 	{0x3232, 0xfc0c},
333e8c08826SMartin Kepplinger 	{0x3236, 0xfc22},
334e8c08826SMartin Kepplinger 	{0x3248, 0xfca8},
335e8c08826SMartin Kepplinger 	{0x326a, 0x8302},
336e8c08826SMartin Kepplinger 	{0x326c, 0x830a},
337e8c08826SMartin Kepplinger 	{0x326e, 0x0000},
338e8c08826SMartin Kepplinger 	{0x32ca, 0xfc28},
339e8c08826SMartin Kepplinger 	{0x32cc, 0xc3bc},
340e8c08826SMartin Kepplinger 	{0x32ce, 0xc34c},
341e8c08826SMartin Kepplinger 	{0x32d0, 0xc35a},
342e8c08826SMartin Kepplinger 	{0x32d2, 0xc368},
343e8c08826SMartin Kepplinger 	{0x32d4, 0xc376},
344e8c08826SMartin Kepplinger 	{0x32d6, 0xc3c2},
345e8c08826SMartin Kepplinger 	{0x32d8, 0xc3e6},
346e8c08826SMartin Kepplinger 	{0x32da, 0x0003},
347e8c08826SMartin Kepplinger 	{0x32dc, 0x0003},
348e8c08826SMartin Kepplinger 	{0x32de, 0x00c7},
349e8c08826SMartin Kepplinger 	{0x32e0, 0x0031},
350e8c08826SMartin Kepplinger 	{0x32e2, 0x0031},
351e8c08826SMartin Kepplinger 	{0x32e4, 0x0031},
352e8c08826SMartin Kepplinger 	{0x32e6, 0xfc28},
353e8c08826SMartin Kepplinger 	{0x32e8, 0xc3bc},
354e8c08826SMartin Kepplinger 	{0x32ea, 0xc384},
355e8c08826SMartin Kepplinger 	{0x32ec, 0xc392},
356e8c08826SMartin Kepplinger 	{0x32ee, 0xc3a0},
357e8c08826SMartin Kepplinger 	{0x32f0, 0xc3ae},
358e8c08826SMartin Kepplinger 	{0x32f2, 0xc3c4},
359e8c08826SMartin Kepplinger 	{0x32f4, 0xc3e6},
360e8c08826SMartin Kepplinger 	{0x32f6, 0x0003},
361e8c08826SMartin Kepplinger 	{0x32f8, 0x0003},
362e8c08826SMartin Kepplinger 	{0x32fa, 0x00c7},
363e8c08826SMartin Kepplinger 	{0x32fc, 0x0031},
364e8c08826SMartin Kepplinger 	{0x32fe, 0x0031},
365e8c08826SMartin Kepplinger 	{0x3300, 0x0031},
366e8c08826SMartin Kepplinger 	{0x3302, 0x82ca},
367e8c08826SMartin Kepplinger 	{0x3304, 0xc164},
368e8c08826SMartin Kepplinger 	{0x3306, 0x82e6},
369e8c08826SMartin Kepplinger 	{0x3308, 0xc19c},
370e8c08826SMartin Kepplinger 	{0x330a, 0x001f},
371e8c08826SMartin Kepplinger 	{0x330c, 0x001a},
372e8c08826SMartin Kepplinger 	{0x330e, 0x0034},
373e8c08826SMartin Kepplinger 	{0x3310, 0x0000},
374e8c08826SMartin Kepplinger 	{0x3312, 0x0000},
375e8c08826SMartin Kepplinger 	{0x3314, 0xfc94},
376e8c08826SMartin Kepplinger 	{0x3316, 0xc3d8},
377e8c08826SMartin Kepplinger 	/* regs above are unknown */
378e8c08826SMartin Kepplinger 	{HI846_REG_MODE_SELECT,			0x0000},
379e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0E04,		0x0012},
380e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_FOBP,		0x1111},
381e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_VACT,		0x1111},
382e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0022,		0x0008},
383e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_START_VACT_H,		0x0040},
384e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0028,		0x0017},
385e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_END_VACT_H,		0x09cf},
386e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_005C,		0x2101},
387e8c08826SMartin Kepplinger 	{HI846_REG_FLL,				0x09de},
388e8c08826SMartin Kepplinger 	{HI846_REG_LLP,				0x0ed8},
389e8c08826SMartin Kepplinger 	{HI846_REG_IMAGE_ORIENTATION,		0x0100},
390e8c08826SMartin Kepplinger 	{HI846_REG_BINNING_MODE,		0x0022},
391e8c08826SMartin Kepplinger 	{HI846_REG_HBIN_MODE,			0x0000},
392e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A24,		0x0000},
393e8c08826SMartin Kepplinger 	{HI846_REG_X_START_H,			0x0000},
394e8c08826SMartin Kepplinger 	{HI846_REG_X_OUTPUT_SIZE_H,		0x0cc0},
395e8c08826SMartin Kepplinger 	{HI846_REG_Y_OUTPUT_SIZE_H,		0x0990},
396e8c08826SMartin Kepplinger 	{HI846_REG_EXPOSURE,			0x09d8},
397e8c08826SMartin Kepplinger 	{HI846_REG_ANALOG_GAIN,			0x0000},
398e8c08826SMartin Kepplinger 	{HI846_REG_GROUPED_PARA_HOLD,		0x0000},
399e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_051E,		0x0000},
400e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0200,		0x0400},
401e8c08826SMartin Kepplinger 	{HI846_REG_PEDESTAL_EN,			0x0c00},
402e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A0C,		0x0010},
403e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A1E,		0x0ccf},
404e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0402,		0x0110},
405e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0404,		0x00f4},
406e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0408,		0x0000},
407e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0410,		0x008d},
408e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0412,		0x011a},
409e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0414,		0x864c},
410e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021C,		0x0003},
411e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021E,		0x0235},
412e8c08826SMartin Kepplinger 	{HI846_REG_BLC_CTL0,			0x9150},
413e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0C06,		0x0021},
414e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0C10,		0x0040},
415e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0C12,		0x0040},
416e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0C14,		0x0040},
417e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0C16,		0x0040},
418e8c08826SMartin Kepplinger 	{HI846_REG_FAST_STANDBY_MODE,		0x0100},
419e8c08826SMartin Kepplinger 	{HI846_REG_ISP_EN_H,			0x014a},
420e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0418,		0x0000},
421e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_012A,		0x03b4},
422e8c08826SMartin Kepplinger 	{HI846_REG_X_ADDR_START_HACT_H,		0x0046},
423e8c08826SMartin Kepplinger 	{HI846_REG_X_ADDR_END_HACT_H,		0x0376},
424e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B02,		0xe04d},
425e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B10,		0x6821},
426e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B12,		0x0120},
427e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B14,		0x0001},
428e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_2008,		0x38fd},
429e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_326E,		0x0000},
430e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0320},
431e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0xc31a},
432e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc109},
433e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x061a},
434e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0306},
435e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0b09},
436e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0c07},
437e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0a00},
438e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x042a},
439e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x006b},
440e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
441e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
442e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca00},
443e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
444e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0F08,		0x2f04},
445e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0F30,		0x001f},
446e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0F36,		0x001f},
447e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0F04,		0x3a00},
448e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_RAMP1_H,		0x025a},
449e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI1_H,		0x025a},
450e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x0024},
451e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_006A,		0x0100},
452e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
453e8c08826SMartin Kepplinger };
454e8c08826SMartin Kepplinger 
455e8c08826SMartin Kepplinger static const struct hi846_reg hi846_init_4lane[] = {
456e8c08826SMartin Kepplinger 	{0x2000, 0x987a},
457e8c08826SMartin Kepplinger 	{0x2002, 0x00ff},
458e8c08826SMartin Kepplinger 	{0x2004, 0x0047},
459e8c08826SMartin Kepplinger 	{0x2006, 0x3fff},
460e8c08826SMartin Kepplinger 	{0x2008, 0x3fff},
461e8c08826SMartin Kepplinger 	{0x200a, 0xc216},
462e8c08826SMartin Kepplinger 	{0x200c, 0x1292},
463e8c08826SMartin Kepplinger 	{0x200e, 0xc01a},
464e8c08826SMartin Kepplinger 	{0x2010, 0x403d},
465e8c08826SMartin Kepplinger 	{0x2012, 0x000e},
466e8c08826SMartin Kepplinger 	{0x2014, 0x403e},
467e8c08826SMartin Kepplinger 	{0x2016, 0x0b80},
468e8c08826SMartin Kepplinger 	{0x2018, 0x403f},
469e8c08826SMartin Kepplinger 	{0x201a, 0x82ae},
470e8c08826SMartin Kepplinger 	{0x201c, 0x1292},
471e8c08826SMartin Kepplinger 	{0x201e, 0xc00c},
472e8c08826SMartin Kepplinger 	{0x2020, 0x4130},
473e8c08826SMartin Kepplinger 	{0x2022, 0x43e2},
474e8c08826SMartin Kepplinger 	{0x2024, 0x0180},
475e8c08826SMartin Kepplinger 	{0x2026, 0x4130},
476e8c08826SMartin Kepplinger 	{0x2028, 0x7400},
477e8c08826SMartin Kepplinger 	{0x202a, 0x5000},
478e8c08826SMartin Kepplinger 	{0x202c, 0x0253},
479e8c08826SMartin Kepplinger 	{0x202e, 0x0ad1},
480e8c08826SMartin Kepplinger 	{0x2030, 0x2360},
481e8c08826SMartin Kepplinger 	{0x2032, 0x0009},
482e8c08826SMartin Kepplinger 	{0x2034, 0x5020},
483e8c08826SMartin Kepplinger 	{0x2036, 0x000b},
484e8c08826SMartin Kepplinger 	{0x2038, 0x0002},
485e8c08826SMartin Kepplinger 	{0x203a, 0x0044},
486e8c08826SMartin Kepplinger 	{0x203c, 0x0016},
487e8c08826SMartin Kepplinger 	{0x203e, 0x1792},
488e8c08826SMartin Kepplinger 	{0x2040, 0x7002},
489e8c08826SMartin Kepplinger 	{0x2042, 0x154f},
490e8c08826SMartin Kepplinger 	{0x2044, 0x00d5},
491e8c08826SMartin Kepplinger 	{0x2046, 0x000b},
492e8c08826SMartin Kepplinger 	{0x2048, 0x0019},
493e8c08826SMartin Kepplinger 	{0x204a, 0x1698},
494e8c08826SMartin Kepplinger 	{0x204c, 0x000e},
495e8c08826SMartin Kepplinger 	{0x204e, 0x099a},
496e8c08826SMartin Kepplinger 	{0x2050, 0x0058},
497e8c08826SMartin Kepplinger 	{0x2052, 0x7000},
498e8c08826SMartin Kepplinger 	{0x2054, 0x1799},
499e8c08826SMartin Kepplinger 	{0x2056, 0x0310},
500e8c08826SMartin Kepplinger 	{0x2058, 0x03c3},
501e8c08826SMartin Kepplinger 	{0x205a, 0x004c},
502e8c08826SMartin Kepplinger 	{0x205c, 0x064a},
503e8c08826SMartin Kepplinger 	{0x205e, 0x0001},
504e8c08826SMartin Kepplinger 	{0x2060, 0x0007},
505e8c08826SMartin Kepplinger 	{0x2062, 0x0bc7},
506e8c08826SMartin Kepplinger 	{0x2064, 0x0055},
507e8c08826SMartin Kepplinger 	{0x2066, 0x7000},
508e8c08826SMartin Kepplinger 	{0x2068, 0x1550},
509e8c08826SMartin Kepplinger 	{0x206a, 0x158a},
510e8c08826SMartin Kepplinger 	{0x206c, 0x0004},
511e8c08826SMartin Kepplinger 	{0x206e, 0x1488},
512e8c08826SMartin Kepplinger 	{0x2070, 0x7010},
513e8c08826SMartin Kepplinger 	{0x2072, 0x1508},
514e8c08826SMartin Kepplinger 	{0x2074, 0x0004},
515e8c08826SMartin Kepplinger 	{0x2076, 0x0016},
516e8c08826SMartin Kepplinger 	{0x2078, 0x03d5},
517e8c08826SMartin Kepplinger 	{0x207a, 0x0055},
518e8c08826SMartin Kepplinger 	{0x207c, 0x08ca},
519e8c08826SMartin Kepplinger 	{0x207e, 0x2019},
520e8c08826SMartin Kepplinger 	{0x2080, 0x0007},
521e8c08826SMartin Kepplinger 	{0x2082, 0x7057},
522e8c08826SMartin Kepplinger 	{0x2084, 0x0fc7},
523e8c08826SMartin Kepplinger 	{0x2086, 0x5041},
524e8c08826SMartin Kepplinger 	{0x2088, 0x12c8},
525e8c08826SMartin Kepplinger 	{0x208a, 0x5060},
526e8c08826SMartin Kepplinger 	{0x208c, 0x5080},
527e8c08826SMartin Kepplinger 	{0x208e, 0x2084},
528e8c08826SMartin Kepplinger 	{0x2090, 0x12c8},
529e8c08826SMartin Kepplinger 	{0x2092, 0x7800},
530e8c08826SMartin Kepplinger 	{0x2094, 0x0802},
531e8c08826SMartin Kepplinger 	{0x2096, 0x040f},
532e8c08826SMartin Kepplinger 	{0x2098, 0x1007},
533e8c08826SMartin Kepplinger 	{0x209a, 0x0803},
534e8c08826SMartin Kepplinger 	{0x209c, 0x080b},
535e8c08826SMartin Kepplinger 	{0x209e, 0x3803},
536e8c08826SMartin Kepplinger 	{0x20a0, 0x0807},
537e8c08826SMartin Kepplinger 	{0x20a2, 0x0404},
538e8c08826SMartin Kepplinger 	{0x20a4, 0x0400},
539e8c08826SMartin Kepplinger 	{0x20a6, 0xffff},
540e8c08826SMartin Kepplinger 	{0x20a8, 0xf0b2},
541e8c08826SMartin Kepplinger 	{0x20aa, 0xffef},
542e8c08826SMartin Kepplinger 	{0x20ac, 0x0a84},
543e8c08826SMartin Kepplinger 	{0x20ae, 0x1292},
544e8c08826SMartin Kepplinger 	{0x20b0, 0xc02e},
545e8c08826SMartin Kepplinger 	{0x20b2, 0x4130},
546e8c08826SMartin Kepplinger 	{0x20b4, 0xf0b2},
547e8c08826SMartin Kepplinger 	{0x20b6, 0xffbf},
548e8c08826SMartin Kepplinger 	{0x20b8, 0x2004},
549e8c08826SMartin Kepplinger 	{0x20ba, 0x403f},
550e8c08826SMartin Kepplinger 	{0x20bc, 0x00c3},
551e8c08826SMartin Kepplinger 	{0x20be, 0x4fe2},
552e8c08826SMartin Kepplinger 	{0x20c0, 0x8318},
553e8c08826SMartin Kepplinger 	{0x20c2, 0x43cf},
554e8c08826SMartin Kepplinger 	{0x20c4, 0x0000},
555e8c08826SMartin Kepplinger 	{0x20c6, 0x9382},
556e8c08826SMartin Kepplinger 	{0x20c8, 0xc314},
557e8c08826SMartin Kepplinger 	{0x20ca, 0x2003},
558e8c08826SMartin Kepplinger 	{0x20cc, 0x12b0},
559e8c08826SMartin Kepplinger 	{0x20ce, 0xcab0},
560e8c08826SMartin Kepplinger 	{0x20d0, 0x4130},
561e8c08826SMartin Kepplinger 	{0x20d2, 0x12b0},
562e8c08826SMartin Kepplinger 	{0x20d4, 0xc90a},
563e8c08826SMartin Kepplinger 	{0x20d6, 0x4130},
564e8c08826SMartin Kepplinger 	{0x20d8, 0x42d2},
565e8c08826SMartin Kepplinger 	{0x20da, 0x8318},
566e8c08826SMartin Kepplinger 	{0x20dc, 0x00c3},
567e8c08826SMartin Kepplinger 	{0x20de, 0x9382},
568e8c08826SMartin Kepplinger 	{0x20e0, 0xc314},
569e8c08826SMartin Kepplinger 	{0x20e2, 0x2009},
570e8c08826SMartin Kepplinger 	{0x20e4, 0x120b},
571e8c08826SMartin Kepplinger 	{0x20e6, 0x120a},
572e8c08826SMartin Kepplinger 	{0x20e8, 0x1209},
573e8c08826SMartin Kepplinger 	{0x20ea, 0x1208},
574e8c08826SMartin Kepplinger 	{0x20ec, 0x1207},
575e8c08826SMartin Kepplinger 	{0x20ee, 0x1206},
576e8c08826SMartin Kepplinger 	{0x20f0, 0x4030},
577e8c08826SMartin Kepplinger 	{0x20f2, 0xc15e},
578e8c08826SMartin Kepplinger 	{0x20f4, 0x4130},
579e8c08826SMartin Kepplinger 	{0x20f6, 0x1292},
580e8c08826SMartin Kepplinger 	{0x20f8, 0xc008},
581e8c08826SMartin Kepplinger 	{0x20fa, 0x4130},
582e8c08826SMartin Kepplinger 	{0x20fc, 0x42d2},
583e8c08826SMartin Kepplinger 	{0x20fe, 0x82a1},
584e8c08826SMartin Kepplinger 	{0x2100, 0x00c2},
585e8c08826SMartin Kepplinger 	{0x2102, 0x1292},
586e8c08826SMartin Kepplinger 	{0x2104, 0xc040},
587e8c08826SMartin Kepplinger 	{0x2106, 0x4130},
588e8c08826SMartin Kepplinger 	{0x2108, 0x1292},
589e8c08826SMartin Kepplinger 	{0x210a, 0xc006},
590e8c08826SMartin Kepplinger 	{0x210c, 0x42a2},
591e8c08826SMartin Kepplinger 	{0x210e, 0x7324},
592e8c08826SMartin Kepplinger 	{0x2110, 0x9382},
593e8c08826SMartin Kepplinger 	{0x2112, 0xc314},
594e8c08826SMartin Kepplinger 	{0x2114, 0x2011},
595e8c08826SMartin Kepplinger 	{0x2116, 0x425f},
596e8c08826SMartin Kepplinger 	{0x2118, 0x82a1},
597e8c08826SMartin Kepplinger 	{0x211a, 0xf25f},
598e8c08826SMartin Kepplinger 	{0x211c, 0x00c1},
599e8c08826SMartin Kepplinger 	{0x211e, 0xf35f},
600e8c08826SMartin Kepplinger 	{0x2120, 0x2406},
601e8c08826SMartin Kepplinger 	{0x2122, 0x425f},
602e8c08826SMartin Kepplinger 	{0x2124, 0x00c0},
603e8c08826SMartin Kepplinger 	{0x2126, 0xf37f},
604e8c08826SMartin Kepplinger 	{0x2128, 0x522f},
605e8c08826SMartin Kepplinger 	{0x212a, 0x4f82},
606e8c08826SMartin Kepplinger 	{0x212c, 0x7324},
607e8c08826SMartin Kepplinger 	{0x212e, 0x425f},
608e8c08826SMartin Kepplinger 	{0x2130, 0x82d4},
609e8c08826SMartin Kepplinger 	{0x2132, 0xf35f},
610e8c08826SMartin Kepplinger 	{0x2134, 0x4fc2},
611e8c08826SMartin Kepplinger 	{0x2136, 0x01b3},
612e8c08826SMartin Kepplinger 	{0x2138, 0x93c2},
613e8c08826SMartin Kepplinger 	{0x213a, 0x829f},
614e8c08826SMartin Kepplinger 	{0x213c, 0x2421},
615e8c08826SMartin Kepplinger 	{0x213e, 0x403e},
616e8c08826SMartin Kepplinger 	{0x2140, 0xfffe},
617e8c08826SMartin Kepplinger 	{0x2142, 0x40b2},
618e8c08826SMartin Kepplinger 	{0x2144, 0xec78},
619e8c08826SMartin Kepplinger 	{0x2146, 0x831c},
620e8c08826SMartin Kepplinger 	{0x2148, 0x40b2},
621e8c08826SMartin Kepplinger 	{0x214a, 0xec78},
622e8c08826SMartin Kepplinger 	{0x214c, 0x831e},
623e8c08826SMartin Kepplinger 	{0x214e, 0x40b2},
624e8c08826SMartin Kepplinger 	{0x2150, 0xec78},
625e8c08826SMartin Kepplinger 	{0x2152, 0x8320},
626e8c08826SMartin Kepplinger 	{0x2154, 0xb3d2},
627e8c08826SMartin Kepplinger 	{0x2156, 0x008c},
628e8c08826SMartin Kepplinger 	{0x2158, 0x2405},
629e8c08826SMartin Kepplinger 	{0x215a, 0x4e0f},
630e8c08826SMartin Kepplinger 	{0x215c, 0x503f},
631e8c08826SMartin Kepplinger 	{0x215e, 0xffd8},
632e8c08826SMartin Kepplinger 	{0x2160, 0x4f82},
633e8c08826SMartin Kepplinger 	{0x2162, 0x831c},
634e8c08826SMartin Kepplinger 	{0x2164, 0x90f2},
635e8c08826SMartin Kepplinger 	{0x2166, 0x0003},
636e8c08826SMartin Kepplinger 	{0x2168, 0x008c},
637e8c08826SMartin Kepplinger 	{0x216a, 0x2401},
638e8c08826SMartin Kepplinger 	{0x216c, 0x4130},
639e8c08826SMartin Kepplinger 	{0x216e, 0x421f},
640e8c08826SMartin Kepplinger 	{0x2170, 0x831c},
641e8c08826SMartin Kepplinger 	{0x2172, 0x5e0f},
642e8c08826SMartin Kepplinger 	{0x2174, 0x4f82},
643e8c08826SMartin Kepplinger 	{0x2176, 0x831e},
644e8c08826SMartin Kepplinger 	{0x2178, 0x5e0f},
645e8c08826SMartin Kepplinger 	{0x217a, 0x4f82},
646e8c08826SMartin Kepplinger 	{0x217c, 0x8320},
647e8c08826SMartin Kepplinger 	{0x217e, 0x3ff6},
648e8c08826SMartin Kepplinger 	{0x2180, 0x432e},
649e8c08826SMartin Kepplinger 	{0x2182, 0x3fdf},
650e8c08826SMartin Kepplinger 	{0x2184, 0x421f},
651e8c08826SMartin Kepplinger 	{0x2186, 0x7100},
652e8c08826SMartin Kepplinger 	{0x2188, 0x4f0e},
653e8c08826SMartin Kepplinger 	{0x218a, 0x503e},
654e8c08826SMartin Kepplinger 	{0x218c, 0xffd8},
655e8c08826SMartin Kepplinger 	{0x218e, 0x4e82},
656e8c08826SMartin Kepplinger 	{0x2190, 0x7a04},
657e8c08826SMartin Kepplinger 	{0x2192, 0x421e},
658e8c08826SMartin Kepplinger 	{0x2194, 0x831c},
659e8c08826SMartin Kepplinger 	{0x2196, 0x5f0e},
660e8c08826SMartin Kepplinger 	{0x2198, 0x4e82},
661e8c08826SMartin Kepplinger 	{0x219a, 0x7a06},
662e8c08826SMartin Kepplinger 	{0x219c, 0x0b00},
663e8c08826SMartin Kepplinger 	{0x219e, 0x7304},
664e8c08826SMartin Kepplinger 	{0x21a0, 0x0050},
665e8c08826SMartin Kepplinger 	{0x21a2, 0x40b2},
666e8c08826SMartin Kepplinger 	{0x21a4, 0xd081},
667e8c08826SMartin Kepplinger 	{0x21a6, 0x0b88},
668e8c08826SMartin Kepplinger 	{0x21a8, 0x421e},
669e8c08826SMartin Kepplinger 	{0x21aa, 0x831e},
670e8c08826SMartin Kepplinger 	{0x21ac, 0x5f0e},
671e8c08826SMartin Kepplinger 	{0x21ae, 0x4e82},
672e8c08826SMartin Kepplinger 	{0x21b0, 0x7a0e},
673e8c08826SMartin Kepplinger 	{0x21b2, 0x521f},
674e8c08826SMartin Kepplinger 	{0x21b4, 0x8320},
675e8c08826SMartin Kepplinger 	{0x21b6, 0x4f82},
676e8c08826SMartin Kepplinger 	{0x21b8, 0x7a10},
677e8c08826SMartin Kepplinger 	{0x21ba, 0x0b00},
678e8c08826SMartin Kepplinger 	{0x21bc, 0x7304},
679e8c08826SMartin Kepplinger 	{0x21be, 0x007a},
680e8c08826SMartin Kepplinger 	{0x21c0, 0x40b2},
681e8c08826SMartin Kepplinger 	{0x21c2, 0x0081},
682e8c08826SMartin Kepplinger 	{0x21c4, 0x0b88},
683e8c08826SMartin Kepplinger 	{0x21c6, 0x4392},
684e8c08826SMartin Kepplinger 	{0x21c8, 0x7a0a},
685e8c08826SMartin Kepplinger 	{0x21ca, 0x0800},
686e8c08826SMartin Kepplinger 	{0x21cc, 0x7a0c},
687e8c08826SMartin Kepplinger 	{0x21ce, 0x0b00},
688e8c08826SMartin Kepplinger 	{0x21d0, 0x7304},
689e8c08826SMartin Kepplinger 	{0x21d2, 0x022b},
690e8c08826SMartin Kepplinger 	{0x21d4, 0x40b2},
691e8c08826SMartin Kepplinger 	{0x21d6, 0xd081},
692e8c08826SMartin Kepplinger 	{0x21d8, 0x0b88},
693e8c08826SMartin Kepplinger 	{0x21da, 0x0b00},
694e8c08826SMartin Kepplinger 	{0x21dc, 0x7304},
695e8c08826SMartin Kepplinger 	{0x21de, 0x0255},
696e8c08826SMartin Kepplinger 	{0x21e0, 0x40b2},
697e8c08826SMartin Kepplinger 	{0x21e2, 0x0081},
698e8c08826SMartin Kepplinger 	{0x21e4, 0x0b88},
699e8c08826SMartin Kepplinger 	{0x21e6, 0x4130},
700e8c08826SMartin Kepplinger 	{0x23fe, 0xc056},
701e8c08826SMartin Kepplinger 	{0x3232, 0xfc0c},
702e8c08826SMartin Kepplinger 	{0x3236, 0xfc22},
703e8c08826SMartin Kepplinger 	{0x3238, 0xfcfc},
704e8c08826SMartin Kepplinger 	{0x323a, 0xfd84},
705e8c08826SMartin Kepplinger 	{0x323c, 0xfd08},
706e8c08826SMartin Kepplinger 	{0x3246, 0xfcd8},
707e8c08826SMartin Kepplinger 	{0x3248, 0xfca8},
708e8c08826SMartin Kepplinger 	{0x324e, 0xfcb4},
709e8c08826SMartin Kepplinger 	{0x326a, 0x8302},
710e8c08826SMartin Kepplinger 	{0x326c, 0x830a},
711e8c08826SMartin Kepplinger 	{0x326e, 0x0000},
712e8c08826SMartin Kepplinger 	{0x32ca, 0xfc28},
713e8c08826SMartin Kepplinger 	{0x32cc, 0xc3bc},
714e8c08826SMartin Kepplinger 	{0x32ce, 0xc34c},
715e8c08826SMartin Kepplinger 	{0x32d0, 0xc35a},
716e8c08826SMartin Kepplinger 	{0x32d2, 0xc368},
717e8c08826SMartin Kepplinger 	{0x32d4, 0xc376},
718e8c08826SMartin Kepplinger 	{0x32d6, 0xc3c2},
719e8c08826SMartin Kepplinger 	{0x32d8, 0xc3e6},
720e8c08826SMartin Kepplinger 	{0x32da, 0x0003},
721e8c08826SMartin Kepplinger 	{0x32dc, 0x0003},
722e8c08826SMartin Kepplinger 	{0x32de, 0x00c7},
723e8c08826SMartin Kepplinger 	{0x32e0, 0x0031},
724e8c08826SMartin Kepplinger 	{0x32e2, 0x0031},
725e8c08826SMartin Kepplinger 	{0x32e4, 0x0031},
726e8c08826SMartin Kepplinger 	{0x32e6, 0xfc28},
727e8c08826SMartin Kepplinger 	{0x32e8, 0xc3bc},
728e8c08826SMartin Kepplinger 	{0x32ea, 0xc384},
729e8c08826SMartin Kepplinger 	{0x32ec, 0xc392},
730e8c08826SMartin Kepplinger 	{0x32ee, 0xc3a0},
731e8c08826SMartin Kepplinger 	{0x32f0, 0xc3ae},
732e8c08826SMartin Kepplinger 	{0x32f2, 0xc3c4},
733e8c08826SMartin Kepplinger 	{0x32f4, 0xc3e6},
734e8c08826SMartin Kepplinger 	{0x32f6, 0x0003},
735e8c08826SMartin Kepplinger 	{0x32f8, 0x0003},
736e8c08826SMartin Kepplinger 	{0x32fa, 0x00c7},
737e8c08826SMartin Kepplinger 	{0x32fc, 0x0031},
738e8c08826SMartin Kepplinger 	{0x32fe, 0x0031},
739e8c08826SMartin Kepplinger 	{0x3300, 0x0031},
740e8c08826SMartin Kepplinger 	{0x3302, 0x82ca},
741e8c08826SMartin Kepplinger 	{0x3304, 0xc164},
742e8c08826SMartin Kepplinger 	{0x3306, 0x82e6},
743e8c08826SMartin Kepplinger 	{0x3308, 0xc19c},
744e8c08826SMartin Kepplinger 	{0x330a, 0x001f},
745e8c08826SMartin Kepplinger 	{0x330c, 0x001a},
746e8c08826SMartin Kepplinger 	{0x330e, 0x0034},
747e8c08826SMartin Kepplinger 	{0x3310, 0x0000},
748e8c08826SMartin Kepplinger 	{0x3312, 0x0000},
749e8c08826SMartin Kepplinger 	{0x3314, 0xfc94},
750e8c08826SMartin Kepplinger 	{0x3316, 0xc3d8},
751e8c08826SMartin Kepplinger 
752e8c08826SMartin Kepplinger 	{0x0a00, 0x0000},
753e8c08826SMartin Kepplinger 	{0x0e04, 0x0012},
754e8c08826SMartin Kepplinger 	{0x002e, 0x1111},
755e8c08826SMartin Kepplinger 	{0x0032, 0x1111},
756e8c08826SMartin Kepplinger 	{0x0022, 0x0008},
757e8c08826SMartin Kepplinger 	{0x0026, 0x0040},
758e8c08826SMartin Kepplinger 	{0x0028, 0x0017},
759e8c08826SMartin Kepplinger 	{0x002c, 0x09cf},
760e8c08826SMartin Kepplinger 	{0x005c, 0x2101},
761e8c08826SMartin Kepplinger 	{0x0006, 0x09de},
762e8c08826SMartin Kepplinger 	{0x0008, 0x0ed8},
763e8c08826SMartin Kepplinger 	{0x000e, 0x0100},
764e8c08826SMartin Kepplinger 	{0x000c, 0x0022},
765e8c08826SMartin Kepplinger 	{0x0a22, 0x0000},
766e8c08826SMartin Kepplinger 	{0x0a24, 0x0000},
767e8c08826SMartin Kepplinger 	{0x0804, 0x0000},
768e8c08826SMartin Kepplinger 	{0x0a12, 0x0cc0},
769e8c08826SMartin Kepplinger 	{0x0a14, 0x0990},
770e8c08826SMartin Kepplinger 	{0x0074, 0x09d8},
771e8c08826SMartin Kepplinger 	{0x0076, 0x0000},
772e8c08826SMartin Kepplinger 	{0x051e, 0x0000},
773e8c08826SMartin Kepplinger 	{0x0200, 0x0400},
774e8c08826SMartin Kepplinger 	{0x0a1a, 0x0c00},
775e8c08826SMartin Kepplinger 	{0x0a0c, 0x0010},
776e8c08826SMartin Kepplinger 	{0x0a1e, 0x0ccf},
777e8c08826SMartin Kepplinger 	{0x0402, 0x0110},
778e8c08826SMartin Kepplinger 	{0x0404, 0x00f4},
779e8c08826SMartin Kepplinger 	{0x0408, 0x0000},
780e8c08826SMartin Kepplinger 	{0x0410, 0x008d},
781e8c08826SMartin Kepplinger 	{0x0412, 0x011a},
782e8c08826SMartin Kepplinger 	{0x0414, 0x864c},
783e8c08826SMartin Kepplinger 	/* for OTP */
784e8c08826SMartin Kepplinger 	{0x021c, 0x0003},
785e8c08826SMartin Kepplinger 	{0x021e, 0x0235},
786e8c08826SMartin Kepplinger 	/* for OTP */
787e8c08826SMartin Kepplinger 	{0x0c00, 0x9950},
788e8c08826SMartin Kepplinger 	{0x0c06, 0x0021},
789e8c08826SMartin Kepplinger 	{0x0c10, 0x0040},
790e8c08826SMartin Kepplinger 	{0x0c12, 0x0040},
791e8c08826SMartin Kepplinger 	{0x0c14, 0x0040},
792e8c08826SMartin Kepplinger 	{0x0c16, 0x0040},
793e8c08826SMartin Kepplinger 	{0x0a02, 0x0100},
794e8c08826SMartin Kepplinger 	{0x0a04, 0x015a},
795e8c08826SMartin Kepplinger 	{0x0418, 0x0000},
796e8c08826SMartin Kepplinger 	{0x0128, 0x0028},
797e8c08826SMartin Kepplinger 	{0x012a, 0xffff},
798e8c08826SMartin Kepplinger 	{0x0120, 0x0046},
799e8c08826SMartin Kepplinger 	{0x0122, 0x0376},
800e8c08826SMartin Kepplinger 	{0x012c, 0x0020},
801e8c08826SMartin Kepplinger 	{0x012e, 0xffff},
802e8c08826SMartin Kepplinger 	{0x0124, 0x0040},
803e8c08826SMartin Kepplinger 	{0x0126, 0x0378},
804e8c08826SMartin Kepplinger 	{0x0746, 0x0050},
805e8c08826SMartin Kepplinger 	{0x0748, 0x01d5},
806e8c08826SMartin Kepplinger 	{0x074a, 0x022b},
807e8c08826SMartin Kepplinger 	{0x074c, 0x03b0},
808e8c08826SMartin Kepplinger 	{0x0756, 0x043f},
809e8c08826SMartin Kepplinger 	{0x0758, 0x3f1d},
810e8c08826SMartin Kepplinger 	{0x0b02, 0xe04d},
811e8c08826SMartin Kepplinger 	{0x0b10, 0x6821},
812e8c08826SMartin Kepplinger 	{0x0b12, 0x0120},
813e8c08826SMartin Kepplinger 	{0x0b14, 0x0001},
814e8c08826SMartin Kepplinger 	{0x2008, 0x38fd},
815e8c08826SMartin Kepplinger 	{0x326e, 0x0000},
816e8c08826SMartin Kepplinger 	{0x0900, 0x0300},
817e8c08826SMartin Kepplinger 	{0x0902, 0xc319},
818e8c08826SMartin Kepplinger 	{0x0914, 0xc109},
819e8c08826SMartin Kepplinger 	{0x0916, 0x061a},
820e8c08826SMartin Kepplinger 	{0x0918, 0x0407},
821e8c08826SMartin Kepplinger 	{0x091a, 0x0a0b},
822e8c08826SMartin Kepplinger 	{0x091c, 0x0e08},
823e8c08826SMartin Kepplinger 	{0x091e, 0x0a00},
824e8c08826SMartin Kepplinger 	{0x090c, 0x0427},
825e8c08826SMartin Kepplinger 	{0x090e, 0x0059},
826e8c08826SMartin Kepplinger 	{0x0954, 0x0089},
827e8c08826SMartin Kepplinger 	{0x0956, 0x0000},
828e8c08826SMartin Kepplinger 	{0x0958, 0xca80},
829e8c08826SMartin Kepplinger 	{0x095a, 0x9240},
830e8c08826SMartin Kepplinger 	{0x0f08, 0x2f04},
831e8c08826SMartin Kepplinger 	{0x0f30, 0x001f},
832e8c08826SMartin Kepplinger 	{0x0f36, 0x001f},
833e8c08826SMartin Kepplinger 	{0x0f04, 0x3a00},
834e8c08826SMartin Kepplinger 	{0x0f32, 0x025a},
835e8c08826SMartin Kepplinger 	{0x0f38, 0x025a},
836e8c08826SMartin Kepplinger 	{0x0f2a, 0x4124},
837e8c08826SMartin Kepplinger 	{0x006a, 0x0100},
838e8c08826SMartin Kepplinger 	{0x004c, 0x0100},
839e8c08826SMartin Kepplinger 	{0x0044, 0x0001},
840e8c08826SMartin Kepplinger };
841e8c08826SMartin Kepplinger 
842e8c08826SMartin Kepplinger static const struct hi846_reg mode_640x480_config[] = {
843e8c08826SMartin Kepplinger 	{HI846_REG_MODE_SELECT,			0x0000},
844e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_FOBP,		0x7711},
845e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_VACT,		0x7711},
846e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_START_VACT_H,		0x0148},
847e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_END_VACT_H,		0x08c7},
848e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_005C,		0x4404},
849e8c08826SMartin Kepplinger 	{HI846_REG_FLL,				0x0277},
850e8c08826SMartin Kepplinger 	{HI846_REG_LLP,				0x0ed8},
851e8c08826SMartin Kepplinger 	{HI846_REG_BINNING_MODE,		0x0322},
852e8c08826SMartin Kepplinger 	{HI846_REG_HBIN_MODE,			0x0200},
853e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A24,		0x0000},
854e8c08826SMartin Kepplinger 	{HI846_REG_X_START_H,			0x0058},
855e8c08826SMartin Kepplinger 	{HI846_REG_X_OUTPUT_SIZE_H,		0x0280},
856e8c08826SMartin Kepplinger 	{HI846_REG_Y_OUTPUT_SIZE_H,		0x01e0},
857e8c08826SMartin Kepplinger 
858e8c08826SMartin Kepplinger 	/* For OTP */
859e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021C,		0x0003},
860e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021E,		0x0235},
861e8c08826SMartin Kepplinger 
862e8c08826SMartin Kepplinger 	{HI846_REG_ISP_EN_H,			0x016a},
863e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0418,		0x0210},
864e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B02,		0xe04d},
865e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B10,		0x7021},
866e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B12,		0x0120},
867e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B14,		0x0001},
868e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_2008,		0x38fd},
869e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_326E,		0x0000},
870e8c08826SMartin Kepplinger };
871e8c08826SMartin Kepplinger 
872e8c08826SMartin Kepplinger static const struct hi846_reg mode_640x480_mipi_2lane[] = {
873e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0300},
874e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0x4319},
875e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc105},
876e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x030c},
877e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0304},
878e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0708},
879e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0b04},
880e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0500},
881e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x0208},
882e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x009a},
883e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
884e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
885e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca80},
886e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
887e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x4924},
888e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
889e8c08826SMartin Kepplinger };
890e8c08826SMartin Kepplinger 
891e8c08826SMartin Kepplinger static const struct hi846_reg mode_1280x720_config[] = {
892e8c08826SMartin Kepplinger 	{HI846_REG_MODE_SELECT,			0x0000},
893e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_FOBP,		0x3311},
894e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_VACT,		0x3311},
895e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_START_VACT_H,		0x0238},
896e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_END_VACT_H,		0x07d7},
897e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_005C,		0x4202},
898e8c08826SMartin Kepplinger 	{HI846_REG_FLL,				0x034a},
899e8c08826SMartin Kepplinger 	{HI846_REG_LLP,				0x0ed8},
900e8c08826SMartin Kepplinger 	{HI846_REG_BINNING_MODE,		0x0122},
901e8c08826SMartin Kepplinger 	{HI846_REG_HBIN_MODE,			0x0100},
902e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A24,		0x0000},
903e8c08826SMartin Kepplinger 	{HI846_REG_X_START_H,			0x00b0},
904e8c08826SMartin Kepplinger 	{HI846_REG_X_OUTPUT_SIZE_H,		0x0500},
905e8c08826SMartin Kepplinger 	{HI846_REG_Y_OUTPUT_SIZE_H,		0x02d0},
906e8c08826SMartin Kepplinger 	{HI846_REG_EXPOSURE,			0x0344},
907e8c08826SMartin Kepplinger 
908e8c08826SMartin Kepplinger 	/* For OTP */
909e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021C,		0x0003},
910e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021E,		0x0235},
911e8c08826SMartin Kepplinger 
912e8c08826SMartin Kepplinger 	{HI846_REG_ISP_EN_H,			0x016a},
913e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0418,		0x0410},
914e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B02,		0xe04d},
915e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B10,		0x6c21},
916e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B12,		0x0120},
917e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B14,		0x0005},
918e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_2008,		0x38fd},
919e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_326E,		0x0000},
920e8c08826SMartin Kepplinger };
921e8c08826SMartin Kepplinger 
922e8c08826SMartin Kepplinger static const struct hi846_reg mode_1280x720_mipi_2lane[] = {
923e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0300},
924e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0x4319},
925e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc109},
926e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x061a},
927e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0407},
928e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0a0b},
929e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0e08},
930e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0a00},
931e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x0427},
932e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x0145},
933e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
934e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
935e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca80},
936e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
937e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x4124},
938e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
939e8c08826SMartin Kepplinger };
940e8c08826SMartin Kepplinger 
941e8c08826SMartin Kepplinger static const struct hi846_reg mode_1280x720_mipi_4lane[] = {
942e8c08826SMartin Kepplinger 	/* 360Mbps */
943e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0300},
944e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0xc319},
945e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc105},
946e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x030c},
947e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0304},
948e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0708},
949e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0b04},
950e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0500},
951e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x0208},
952e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x008a},
953e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
954e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
955e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca80},
956e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
957e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x4924},
958e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
959e8c08826SMartin Kepplinger };
960e8c08826SMartin Kepplinger 
961e8c08826SMartin Kepplinger static const struct hi846_reg mode_1632x1224_config[] = {
962e8c08826SMartin Kepplinger 	{HI846_REG_MODE_SELECT,			0x0000},
963e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_FOBP,		0x3311},
964e8c08826SMartin Kepplinger 	{HI846_REG_Y_ODD_INC_VACT,		0x3311},
965e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_START_VACT_H,		0x0040},
966e8c08826SMartin Kepplinger 	{HI846_REG_Y_ADDR_END_VACT_H,		0x09cf},
967e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_005C,		0x4202},
968e8c08826SMartin Kepplinger 	{HI846_REG_FLL,				0x09de},
969e8c08826SMartin Kepplinger 	{HI846_REG_LLP,				0x0ed8},
970e8c08826SMartin Kepplinger 	{HI846_REG_BINNING_MODE,		0x0122},
971e8c08826SMartin Kepplinger 	{HI846_REG_HBIN_MODE,			0x0100},
972e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0A24,		0x0000},
973e8c08826SMartin Kepplinger 	{HI846_REG_X_START_H,			0x0000},
974e8c08826SMartin Kepplinger 	{HI846_REG_X_OUTPUT_SIZE_H,		0x0660},
975e8c08826SMartin Kepplinger 	{HI846_REG_Y_OUTPUT_SIZE_H,		0x04c8},
976e8c08826SMartin Kepplinger 	{HI846_REG_EXPOSURE,			0x09d8},
977e8c08826SMartin Kepplinger 
978e8c08826SMartin Kepplinger 	/* For OTP */
979e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021C,		0x0003},
980e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_021E,		0x0235},
981e8c08826SMartin Kepplinger 
982e8c08826SMartin Kepplinger 	{HI846_REG_ISP_EN_H,			0x016a},
983e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0418,		0x0000},
984e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B02,		0xe04d},
985e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B10,		0x6c21},
986e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B12,		0x0120},
987e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0B14,		0x0005},
988e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_2008,		0x38fd},
989e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_326E,		0x0000},
990e8c08826SMartin Kepplinger };
991e8c08826SMartin Kepplinger 
992e8c08826SMartin Kepplinger static const struct hi846_reg mode_1632x1224_mipi_2lane[] = {
993e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0300},
994e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0x4319},
995e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc109},
996e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x061a},
997e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0407},
998e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0a0b},
999e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0e08},
1000e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0a00},
1001e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x0427},
1002e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x0069},
1003e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
1004e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
1005e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca80},
1006e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
1007e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x4124},
1008e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
1009e8c08826SMartin Kepplinger };
1010e8c08826SMartin Kepplinger 
1011e8c08826SMartin Kepplinger static const struct hi846_reg mode_1632x1224_mipi_4lane[] = {
1012e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0900,		0x0300},
1013e8c08826SMartin Kepplinger 	{HI846_REG_MIPI_TX_OP_MODE,		0xc319},
1014e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0914,		0xc105},
1015e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_PREPARE,		0x030c},
1016e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0918,		0x0304},
1017e8c08826SMartin Kepplinger 	{HI846_REG_THS_ZERO,			0x0708},
1018e8c08826SMartin Kepplinger 	{HI846_REG_TCLK_POST,			0x0b04},
1019e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_091E,		0x0500},
1020e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090C,		0x0208},
1021e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_090E,		0x001c},
1022e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0954,		0x0089},
1023e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0956,		0x0000},
1024e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_0958,		0xca80},
1025e8c08826SMartin Kepplinger 	{HI846_REG_UNKNOWN_095A,		0x9240},
1026e8c08826SMartin Kepplinger 	{HI846_REG_PLL_CFG_MIPI2_H,		0x4924},
1027e8c08826SMartin Kepplinger 	{HI846_REG_TG_ENABLE,			0x0100},
1028e8c08826SMartin Kepplinger };
1029e8c08826SMartin Kepplinger 
1030e8c08826SMartin Kepplinger static const char * const hi846_test_pattern_menu[] = {
1031e8c08826SMartin Kepplinger 	"Disabled",
1032e8c08826SMartin Kepplinger 	"Solid Colour",
1033e8c08826SMartin Kepplinger 	"100% Colour Bars",
1034e8c08826SMartin Kepplinger 	"Fade To Grey Colour Bars",
1035e8c08826SMartin Kepplinger 	"PN9",
1036e8c08826SMartin Kepplinger 	"Gradient Horizontal",
1037e8c08826SMartin Kepplinger 	"Gradient Vertical",
1038e8c08826SMartin Kepplinger 	"Check Board",
1039e8c08826SMartin Kepplinger 	"Slant Pattern",
1040e8c08826SMartin Kepplinger 	"Resolution Pattern",
1041e8c08826SMartin Kepplinger };
1042e8c08826SMartin Kepplinger 
1043e8c08826SMartin Kepplinger #define FREQ_INDEX_640	0
1044e8c08826SMartin Kepplinger #define FREQ_INDEX_1280	1
1045e8c08826SMartin Kepplinger static const s64 hi846_link_freqs[] = {
1046e8c08826SMartin Kepplinger 	[FREQ_INDEX_640] = 80000000,
1047e8c08826SMartin Kepplinger 	[FREQ_INDEX_1280] = 200000000,
1048e8c08826SMartin Kepplinger };
1049e8c08826SMartin Kepplinger 
1050e8c08826SMartin Kepplinger static const struct hi846_reg_list hi846_init_regs_list_2lane = {
1051e8c08826SMartin Kepplinger 	.num_of_regs = ARRAY_SIZE(hi846_init_2lane),
1052e8c08826SMartin Kepplinger 	.regs = hi846_init_2lane,
1053e8c08826SMartin Kepplinger };
1054e8c08826SMartin Kepplinger 
1055e8c08826SMartin Kepplinger static const struct hi846_reg_list hi846_init_regs_list_4lane = {
1056e8c08826SMartin Kepplinger 	.num_of_regs = ARRAY_SIZE(hi846_init_4lane),
1057e8c08826SMartin Kepplinger 	.regs = hi846_init_4lane,
1058e8c08826SMartin Kepplinger };
1059e8c08826SMartin Kepplinger 
1060e8c08826SMartin Kepplinger static const struct hi846_mode supported_modes[] = {
1061e8c08826SMartin Kepplinger 	{
1062e8c08826SMartin Kepplinger 		.width = 640,
1063e8c08826SMartin Kepplinger 		.height = 480,
1064e8c08826SMartin Kepplinger 		.link_freq_index = FREQ_INDEX_640,
1065e8c08826SMartin Kepplinger 		.fps = 120,
1066e8c08826SMartin Kepplinger 		.frame_len = 631,
1067e8c08826SMartin Kepplinger 		.llp = HI846_LINE_LENGTH,
1068e8c08826SMartin Kepplinger 		.reg_list_config = {
1069e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_640x480_config),
1070e8c08826SMartin Kepplinger 			.regs = mode_640x480_config,
1071e8c08826SMartin Kepplinger 		},
1072e8c08826SMartin Kepplinger 		.reg_list_2lane = {
1073e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_640x480_mipi_2lane),
1074e8c08826SMartin Kepplinger 			.regs = mode_640x480_mipi_2lane,
1075e8c08826SMartin Kepplinger 		},
1076e8c08826SMartin Kepplinger 		.reg_list_4lane = {
1077e8c08826SMartin Kepplinger 			.num_of_regs = 0,
1078e8c08826SMartin Kepplinger 		},
1079e8c08826SMartin Kepplinger 		.crop = {
1080e8c08826SMartin Kepplinger 			.left = 0x58,
1081e8c08826SMartin Kepplinger 			.top = 0x148,
1082e8c08826SMartin Kepplinger 			.width = 640 * 4,
1083e8c08826SMartin Kepplinger 			.height = 480 * 4,
1084e8c08826SMartin Kepplinger 		},
1085e8c08826SMartin Kepplinger 	},
1086e8c08826SMartin Kepplinger 	{
1087e8c08826SMartin Kepplinger 		.width = 1280,
1088e8c08826SMartin Kepplinger 		.height = 720,
1089e8c08826SMartin Kepplinger 		.link_freq_index = FREQ_INDEX_1280,
1090e8c08826SMartin Kepplinger 		.fps = 90,
1091e8c08826SMartin Kepplinger 		.frame_len = 842,
1092e8c08826SMartin Kepplinger 		.llp = HI846_LINE_LENGTH,
1093e8c08826SMartin Kepplinger 		.reg_list_config = {
1094e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1280x720_config),
1095e8c08826SMartin Kepplinger 			.regs = mode_1280x720_config,
1096e8c08826SMartin Kepplinger 		},
1097e8c08826SMartin Kepplinger 		.reg_list_2lane = {
1098e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1280x720_mipi_2lane),
1099e8c08826SMartin Kepplinger 			.regs = mode_1280x720_mipi_2lane,
1100e8c08826SMartin Kepplinger 		},
1101e8c08826SMartin Kepplinger 		.reg_list_4lane = {
1102e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1280x720_mipi_4lane),
1103e8c08826SMartin Kepplinger 			.regs = mode_1280x720_mipi_4lane,
1104e8c08826SMartin Kepplinger 		},
1105e8c08826SMartin Kepplinger 		.crop = {
1106e8c08826SMartin Kepplinger 			.left = 0xb0,
1107e8c08826SMartin Kepplinger 			.top = 0x238,
1108e8c08826SMartin Kepplinger 			.width = 1280 * 2,
1109e8c08826SMartin Kepplinger 			.height = 720 * 2,
1110e8c08826SMartin Kepplinger 		},
1111e8c08826SMartin Kepplinger 	},
1112e8c08826SMartin Kepplinger 	{
1113e8c08826SMartin Kepplinger 		.width = 1632,
1114e8c08826SMartin Kepplinger 		.height = 1224,
1115e8c08826SMartin Kepplinger 		.link_freq_index = FREQ_INDEX_1280,
1116e8c08826SMartin Kepplinger 		.fps = 30,
1117e8c08826SMartin Kepplinger 		.frame_len = 2526,
1118e8c08826SMartin Kepplinger 		.llp = HI846_LINE_LENGTH,
1119e8c08826SMartin Kepplinger 		.reg_list_config = {
1120e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1632x1224_config),
1121e8c08826SMartin Kepplinger 			.regs = mode_1632x1224_config,
1122e8c08826SMartin Kepplinger 		},
1123e8c08826SMartin Kepplinger 		.reg_list_2lane = {
1124e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1632x1224_mipi_2lane),
1125e8c08826SMartin Kepplinger 			.regs = mode_1632x1224_mipi_2lane,
1126e8c08826SMartin Kepplinger 		},
1127e8c08826SMartin Kepplinger 		.reg_list_4lane = {
1128e8c08826SMartin Kepplinger 			.num_of_regs = ARRAY_SIZE(mode_1632x1224_mipi_4lane),
1129e8c08826SMartin Kepplinger 			.regs = mode_1632x1224_mipi_4lane,
1130e8c08826SMartin Kepplinger 		},
1131e8c08826SMartin Kepplinger 		.crop = {
1132e8c08826SMartin Kepplinger 			.left = 0x0,
1133e8c08826SMartin Kepplinger 			.top = 0x0,
1134e8c08826SMartin Kepplinger 			.width = 1632 * 2,
1135e8c08826SMartin Kepplinger 			.height = 1224 * 2,
1136e8c08826SMartin Kepplinger 		},
1137e8c08826SMartin Kepplinger 	}
1138e8c08826SMartin Kepplinger };
1139e8c08826SMartin Kepplinger 
1140e8c08826SMartin Kepplinger struct hi846_datafmt {
1141e8c08826SMartin Kepplinger 	u32 code;
1142e8c08826SMartin Kepplinger 	enum v4l2_colorspace colorspace;
1143e8c08826SMartin Kepplinger };
1144e8c08826SMartin Kepplinger 
1145e8c08826SMartin Kepplinger static const char * const hi846_supply_names[] = {
1146e8c08826SMartin Kepplinger 	"vddio", /* Digital I/O (1.8V or 2.8V) */
1147e8c08826SMartin Kepplinger 	"vdda", /* Analog (2.8V) */
1148e8c08826SMartin Kepplinger 	"vddd", /* Digital Core (1.2V) */
1149e8c08826SMartin Kepplinger };
1150e8c08826SMartin Kepplinger 
1151e8c08826SMartin Kepplinger #define HI846_NUM_SUPPLIES ARRAY_SIZE(hi846_supply_names)
1152e8c08826SMartin Kepplinger 
1153e8c08826SMartin Kepplinger struct hi846 {
1154e8c08826SMartin Kepplinger 	struct gpio_desc *rst_gpio;
1155e8c08826SMartin Kepplinger 	struct gpio_desc *shutdown_gpio;
1156e8c08826SMartin Kepplinger 	struct regulator_bulk_data supplies[HI846_NUM_SUPPLIES];
1157e8c08826SMartin Kepplinger 	struct clk *clock;
1158e8c08826SMartin Kepplinger 	const struct hi846_datafmt *fmt;
1159e8c08826SMartin Kepplinger 	struct v4l2_subdev sd;
1160e8c08826SMartin Kepplinger 	struct media_pad pad;
1161e8c08826SMartin Kepplinger 	struct v4l2_ctrl_handler ctrl_handler;
1162e8c08826SMartin Kepplinger 	u8 nr_lanes;
1163e8c08826SMartin Kepplinger 
1164e8c08826SMartin Kepplinger 	struct v4l2_ctrl *link_freq;
1165e8c08826SMartin Kepplinger 	struct v4l2_ctrl *pixel_rate;
1166e8c08826SMartin Kepplinger 	struct v4l2_ctrl *vblank;
1167e8c08826SMartin Kepplinger 	struct v4l2_ctrl *hblank;
1168e8c08826SMartin Kepplinger 	struct v4l2_ctrl *exposure;
1169e8c08826SMartin Kepplinger 
1170e8c08826SMartin Kepplinger 	struct mutex mutex; /* protect cur_mode, streaming and chip access */
1171e8c08826SMartin Kepplinger 	const struct hi846_mode *cur_mode;
1172e8c08826SMartin Kepplinger 	bool streaming;
1173e8c08826SMartin Kepplinger };
1174e8c08826SMartin Kepplinger 
to_hi846(struct v4l2_subdev * sd)1175e8c08826SMartin Kepplinger static inline struct hi846 *to_hi846(struct v4l2_subdev *sd)
1176e8c08826SMartin Kepplinger {
1177e8c08826SMartin Kepplinger 	return container_of(sd, struct hi846, sd);
1178e8c08826SMartin Kepplinger }
1179e8c08826SMartin Kepplinger 
1180e8c08826SMartin Kepplinger static const struct hi846_datafmt hi846_colour_fmts[] = {
1181e8c08826SMartin Kepplinger 	{ HI846_MEDIA_BUS_FORMAT, V4L2_COLORSPACE_RAW },
1182e8c08826SMartin Kepplinger };
1183e8c08826SMartin Kepplinger 
hi846_find_datafmt(u32 code)1184e8c08826SMartin Kepplinger static const struct hi846_datafmt *hi846_find_datafmt(u32 code)
1185e8c08826SMartin Kepplinger {
1186e8c08826SMartin Kepplinger 	unsigned int i;
1187e8c08826SMartin Kepplinger 
1188e8c08826SMartin Kepplinger 	for (i = 0; i < ARRAY_SIZE(hi846_colour_fmts); i++)
1189e8c08826SMartin Kepplinger 		if (hi846_colour_fmts[i].code == code)
1190e8c08826SMartin Kepplinger 			return &hi846_colour_fmts[i];
1191e8c08826SMartin Kepplinger 
1192e8c08826SMartin Kepplinger 	return NULL;
1193e8c08826SMartin Kepplinger }
1194e8c08826SMartin Kepplinger 
hi846_get_link_freq_index(struct hi846 * hi846)1195e8c08826SMartin Kepplinger static inline u8 hi846_get_link_freq_index(struct hi846 *hi846)
1196e8c08826SMartin Kepplinger {
1197e8c08826SMartin Kepplinger 	return hi846->cur_mode->link_freq_index;
1198e8c08826SMartin Kepplinger }
1199e8c08826SMartin Kepplinger 
hi846_get_link_freq(struct hi846 * hi846)1200e8c08826SMartin Kepplinger static u64 hi846_get_link_freq(struct hi846 *hi846)
1201e8c08826SMartin Kepplinger {
1202e8c08826SMartin Kepplinger 	u8 index = hi846_get_link_freq_index(hi846);
1203e8c08826SMartin Kepplinger 
1204e8c08826SMartin Kepplinger 	return hi846_link_freqs[index];
1205e8c08826SMartin Kepplinger }
1206e8c08826SMartin Kepplinger 
hi846_calc_pixel_rate(struct hi846 * hi846)1207e8c08826SMartin Kepplinger static u64 hi846_calc_pixel_rate(struct hi846 *hi846)
1208e8c08826SMartin Kepplinger {
1209e8c08826SMartin Kepplinger 	u64 link_freq = hi846_get_link_freq(hi846);
1210e8c08826SMartin Kepplinger 	u64 pixel_rate = link_freq * 2 * hi846->nr_lanes;
1211e8c08826SMartin Kepplinger 
1212e8c08826SMartin Kepplinger 	do_div(pixel_rate, HI846_RGB_DEPTH);
1213e8c08826SMartin Kepplinger 
1214e8c08826SMartin Kepplinger 	return pixel_rate;
1215e8c08826SMartin Kepplinger }
1216e8c08826SMartin Kepplinger 
hi846_read_reg(struct hi846 * hi846,u16 reg,u8 * val)1217e8c08826SMartin Kepplinger static int hi846_read_reg(struct hi846 *hi846, u16 reg, u8 *val)
1218e8c08826SMartin Kepplinger {
1219e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1220e8c08826SMartin Kepplinger 	struct i2c_msg msgs[2];
1221e8c08826SMartin Kepplinger 	u8 addr_buf[2];
1222e8c08826SMartin Kepplinger 	u8 data_buf[1] = {0};
1223e8c08826SMartin Kepplinger 	int ret;
1224e8c08826SMartin Kepplinger 
1225e8c08826SMartin Kepplinger 	put_unaligned_be16(reg, addr_buf);
1226e8c08826SMartin Kepplinger 	msgs[0].addr = client->addr;
1227e8c08826SMartin Kepplinger 	msgs[0].flags = 0;
1228e8c08826SMartin Kepplinger 	msgs[0].len = sizeof(addr_buf);
1229e8c08826SMartin Kepplinger 	msgs[0].buf = addr_buf;
1230e8c08826SMartin Kepplinger 	msgs[1].addr = client->addr;
1231e8c08826SMartin Kepplinger 	msgs[1].flags = I2C_M_RD;
1232e8c08826SMartin Kepplinger 	msgs[1].len = 1;
1233e8c08826SMartin Kepplinger 	msgs[1].buf = data_buf;
1234e8c08826SMartin Kepplinger 
1235e8c08826SMartin Kepplinger 	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
1236e8c08826SMartin Kepplinger 	if (ret != ARRAY_SIZE(msgs)) {
1237e8c08826SMartin Kepplinger 		dev_err(&client->dev, "i2c read error: %d\n", ret);
1238e8c08826SMartin Kepplinger 		return -EIO;
1239e8c08826SMartin Kepplinger 	}
1240e8c08826SMartin Kepplinger 
1241e8c08826SMartin Kepplinger 	*val = data_buf[0];
1242e8c08826SMartin Kepplinger 
1243e8c08826SMartin Kepplinger 	return 0;
1244e8c08826SMartin Kepplinger }
1245e8c08826SMartin Kepplinger 
hi846_write_reg(struct hi846 * hi846,u16 reg,u8 val)1246e8c08826SMartin Kepplinger static int hi846_write_reg(struct hi846 *hi846, u16 reg, u8 val)
1247e8c08826SMartin Kepplinger {
1248e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1249e8c08826SMartin Kepplinger 	u8 buf[3] = { reg >> 8, reg & 0xff, val };
1250e8c08826SMartin Kepplinger 	struct i2c_msg msg[] = {
1251e8c08826SMartin Kepplinger 		{ .addr = client->addr, .flags = 0,
1252e8c08826SMartin Kepplinger 		  .len = ARRAY_SIZE(buf), .buf = buf },
1253e8c08826SMartin Kepplinger 	};
1254e8c08826SMartin Kepplinger 	int ret;
1255e8c08826SMartin Kepplinger 
1256e8c08826SMartin Kepplinger 	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
1257e8c08826SMartin Kepplinger 	if (ret != ARRAY_SIZE(msg)) {
1258e8c08826SMartin Kepplinger 		dev_err(&client->dev, "i2c write error\n");
1259e8c08826SMartin Kepplinger 		return -EIO;
1260e8c08826SMartin Kepplinger 	}
1261e8c08826SMartin Kepplinger 
1262e8c08826SMartin Kepplinger 	return 0;
1263e8c08826SMartin Kepplinger }
1264e8c08826SMartin Kepplinger 
hi846_write_reg_16(struct hi846 * hi846,u16 reg,u16 val,int * err)1265e8c08826SMartin Kepplinger static void hi846_write_reg_16(struct hi846 *hi846, u16 reg, u16 val, int *err)
1266e8c08826SMartin Kepplinger {
1267e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1268e8c08826SMartin Kepplinger 	u8 buf[4];
1269e8c08826SMartin Kepplinger 	int ret;
1270e8c08826SMartin Kepplinger 
1271e8c08826SMartin Kepplinger 	if (*err < 0)
1272e8c08826SMartin Kepplinger 		return;
1273e8c08826SMartin Kepplinger 
1274e8c08826SMartin Kepplinger 	put_unaligned_be16(reg, buf);
1275e8c08826SMartin Kepplinger 	put_unaligned_be16(val, buf + 2);
1276e8c08826SMartin Kepplinger 	ret = i2c_master_send(client, buf, sizeof(buf));
1277e8c08826SMartin Kepplinger 	if (ret != sizeof(buf)) {
1278e8c08826SMartin Kepplinger 		dev_err(&client->dev, "i2c_master_send != %zu: %d\n",
1279e8c08826SMartin Kepplinger 			sizeof(buf), ret);
1280e8c08826SMartin Kepplinger 		*err = -EIO;
1281e8c08826SMartin Kepplinger 	}
1282e8c08826SMartin Kepplinger }
1283e8c08826SMartin Kepplinger 
hi846_write_reg_list(struct hi846 * hi846,const struct hi846_reg_list * r_list)1284e8c08826SMartin Kepplinger static int hi846_write_reg_list(struct hi846 *hi846,
1285e8c08826SMartin Kepplinger 				const struct hi846_reg_list *r_list)
1286e8c08826SMartin Kepplinger {
1287e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1288e8c08826SMartin Kepplinger 	unsigned int i;
1289e8c08826SMartin Kepplinger 	int ret = 0;
1290e8c08826SMartin Kepplinger 
1291e8c08826SMartin Kepplinger 	for (i = 0; i < r_list->num_of_regs; i++) {
1292e8c08826SMartin Kepplinger 		hi846_write_reg_16(hi846, r_list->regs[i].address,
1293e8c08826SMartin Kepplinger 				   r_list->regs[i].val, &ret);
1294e8c08826SMartin Kepplinger 		if (ret) {
1295e8c08826SMartin Kepplinger 			dev_err_ratelimited(&client->dev,
1296e8c08826SMartin Kepplinger 					    "failed to write reg 0x%4.4x: %d",
1297e8c08826SMartin Kepplinger 					    r_list->regs[i].address, ret);
1298e8c08826SMartin Kepplinger 			return ret;
1299e8c08826SMartin Kepplinger 		}
1300e8c08826SMartin Kepplinger 	}
1301e8c08826SMartin Kepplinger 
1302e8c08826SMartin Kepplinger 	return 0;
1303e8c08826SMartin Kepplinger }
1304e8c08826SMartin Kepplinger 
hi846_update_digital_gain(struct hi846 * hi846,u16 d_gain)1305e8c08826SMartin Kepplinger static int hi846_update_digital_gain(struct hi846 *hi846, u16 d_gain)
1306e8c08826SMartin Kepplinger {
1307e8c08826SMartin Kepplinger 	int ret = 0;
1308e8c08826SMartin Kepplinger 
1309e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_MWB_GR_GAIN_H, d_gain, &ret);
1310e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_MWB_GB_GAIN_H, d_gain, &ret);
1311e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_MWB_R_GAIN_H, d_gain, &ret);
1312e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_MWB_B_GAIN_H, d_gain, &ret);
1313e8c08826SMartin Kepplinger 
1314e8c08826SMartin Kepplinger 	return ret;
1315e8c08826SMartin Kepplinger }
1316e8c08826SMartin Kepplinger 
hi846_test_pattern(struct hi846 * hi846,u32 pattern)1317e8c08826SMartin Kepplinger static int hi846_test_pattern(struct hi846 *hi846, u32 pattern)
1318e8c08826SMartin Kepplinger {
1319e8c08826SMartin Kepplinger 	int ret;
1320e8c08826SMartin Kepplinger 	u8 val;
1321e8c08826SMartin Kepplinger 
1322e8c08826SMartin Kepplinger 	if (pattern) {
1323e8c08826SMartin Kepplinger 		ret = hi846_read_reg(hi846, HI846_REG_ISP, &val);
1324e8c08826SMartin Kepplinger 		if (ret)
1325e8c08826SMartin Kepplinger 			return ret;
1326e8c08826SMartin Kepplinger 
1327e8c08826SMartin Kepplinger 		ret = hi846_write_reg(hi846, HI846_REG_ISP,
1328e8c08826SMartin Kepplinger 				      val | HI846_REG_ISP_TPG_EN);
1329e8c08826SMartin Kepplinger 		if (ret)
1330e8c08826SMartin Kepplinger 			return ret;
1331e8c08826SMartin Kepplinger 	}
1332e8c08826SMartin Kepplinger 
1333e8c08826SMartin Kepplinger 	return hi846_write_reg(hi846, HI846_REG_TEST_PATTERN, pattern);
1334e8c08826SMartin Kepplinger }
1335e8c08826SMartin Kepplinger 
hi846_set_ctrl(struct v4l2_ctrl * ctrl)1336e8c08826SMartin Kepplinger static int hi846_set_ctrl(struct v4l2_ctrl *ctrl)
1337e8c08826SMartin Kepplinger {
1338e8c08826SMartin Kepplinger 	struct hi846 *hi846 = container_of(ctrl->handler,
1339e8c08826SMartin Kepplinger 					     struct hi846, ctrl_handler);
1340e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1341e8c08826SMartin Kepplinger 	s64 exposure_max;
1342e8c08826SMartin Kepplinger 	int ret = 0;
1343e8c08826SMartin Kepplinger 	u32 shutter, frame_len;
1344e8c08826SMartin Kepplinger 
1345e8c08826SMartin Kepplinger 	/* Propagate change of current control to all related controls */
1346e8c08826SMartin Kepplinger 	if (ctrl->id == V4L2_CID_VBLANK) {
1347e8c08826SMartin Kepplinger 		/* Update max exposure while meeting expected vblanking */
1348e8c08826SMartin Kepplinger 		exposure_max = hi846->cur_mode->height + ctrl->val -
1349e8c08826SMartin Kepplinger 			       HI846_EXPOSURE_MAX_MARGIN;
1350e8c08826SMartin Kepplinger 		__v4l2_ctrl_modify_range(hi846->exposure,
1351e8c08826SMartin Kepplinger 					 hi846->exposure->minimum,
1352e8c08826SMartin Kepplinger 					 exposure_max, hi846->exposure->step,
1353e8c08826SMartin Kepplinger 					 exposure_max);
1354e8c08826SMartin Kepplinger 	}
1355e8c08826SMartin Kepplinger 
1356*04fc06f6SMartin Kepplinger 	ret = pm_runtime_get_if_in_use(&client->dev);
1357*04fc06f6SMartin Kepplinger 	if (!ret || ret == -EAGAIN)
1358e8c08826SMartin Kepplinger 		return 0;
1359e8c08826SMartin Kepplinger 
1360e8c08826SMartin Kepplinger 	switch (ctrl->id) {
1361e8c08826SMartin Kepplinger 	case V4L2_CID_ANALOGUE_GAIN:
1362e8c08826SMartin Kepplinger 		ret = hi846_write_reg(hi846, HI846_REG_ANALOG_GAIN, ctrl->val);
1363e8c08826SMartin Kepplinger 		break;
1364e8c08826SMartin Kepplinger 
1365e8c08826SMartin Kepplinger 	case V4L2_CID_DIGITAL_GAIN:
1366e8c08826SMartin Kepplinger 		ret = hi846_update_digital_gain(hi846, ctrl->val);
1367e8c08826SMartin Kepplinger 		break;
1368e8c08826SMartin Kepplinger 
1369e8c08826SMartin Kepplinger 	case V4L2_CID_EXPOSURE:
1370e8c08826SMartin Kepplinger 		shutter = ctrl->val;
1371e8c08826SMartin Kepplinger 		frame_len = hi846->cur_mode->frame_len;
1372e8c08826SMartin Kepplinger 
1373e8c08826SMartin Kepplinger 		if (shutter > frame_len - 6) { /* margin */
1374e8c08826SMartin Kepplinger 			frame_len = shutter + 6;
1375e8c08826SMartin Kepplinger 			if (frame_len > 0xffff) { /* max frame len */
1376e8c08826SMartin Kepplinger 				frame_len = 0xffff;
1377e8c08826SMartin Kepplinger 			}
1378e8c08826SMartin Kepplinger 		}
1379e8c08826SMartin Kepplinger 
1380e8c08826SMartin Kepplinger 		if (shutter < 6)
1381e8c08826SMartin Kepplinger 			shutter = 6;
1382e8c08826SMartin Kepplinger 		if (shutter > (0xffff - 6))
1383e8c08826SMartin Kepplinger 			shutter = 0xffff - 6;
1384e8c08826SMartin Kepplinger 
1385e8c08826SMartin Kepplinger 		hi846_write_reg_16(hi846, HI846_REG_FLL, frame_len, &ret);
1386e8c08826SMartin Kepplinger 		hi846_write_reg_16(hi846, HI846_REG_EXPOSURE, shutter, &ret);
1387e8c08826SMartin Kepplinger 		break;
1388e8c08826SMartin Kepplinger 
1389e8c08826SMartin Kepplinger 	case V4L2_CID_VBLANK:
1390e8c08826SMartin Kepplinger 		/* Update FLL that meets expected vertical blanking */
1391e8c08826SMartin Kepplinger 		hi846_write_reg_16(hi846, HI846_REG_FLL,
1392e8c08826SMartin Kepplinger 				   hi846->cur_mode->height + ctrl->val, &ret);
1393e8c08826SMartin Kepplinger 		break;
1394e8c08826SMartin Kepplinger 	case V4L2_CID_TEST_PATTERN:
1395e8c08826SMartin Kepplinger 		ret = hi846_test_pattern(hi846, ctrl->val);
1396e8c08826SMartin Kepplinger 		break;
1397e8c08826SMartin Kepplinger 
1398e8c08826SMartin Kepplinger 	default:
1399e8c08826SMartin Kepplinger 		ret = -EINVAL;
1400e8c08826SMartin Kepplinger 		break;
1401e8c08826SMartin Kepplinger 	}
1402e8c08826SMartin Kepplinger 
1403e8c08826SMartin Kepplinger 	pm_runtime_put(&client->dev);
1404e8c08826SMartin Kepplinger 
1405e8c08826SMartin Kepplinger 	return ret;
1406e8c08826SMartin Kepplinger }
1407e8c08826SMartin Kepplinger 
1408e8c08826SMartin Kepplinger static const struct v4l2_ctrl_ops hi846_ctrl_ops = {
1409e8c08826SMartin Kepplinger 	.s_ctrl = hi846_set_ctrl,
1410e8c08826SMartin Kepplinger };
1411e8c08826SMartin Kepplinger 
hi846_init_controls(struct hi846 * hi846)1412e8c08826SMartin Kepplinger static int hi846_init_controls(struct hi846 *hi846)
1413e8c08826SMartin Kepplinger {
1414e8c08826SMartin Kepplinger 	struct v4l2_ctrl_handler *ctrl_hdlr;
1415e8c08826SMartin Kepplinger 	s64 exposure_max, h_blank;
1416e8c08826SMartin Kepplinger 	int ret;
1417e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1418e8c08826SMartin Kepplinger 	struct v4l2_fwnode_device_properties props;
1419e8c08826SMartin Kepplinger 
1420e8c08826SMartin Kepplinger 	ctrl_hdlr = &hi846->ctrl_handler;
1421e8c08826SMartin Kepplinger 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
1422e8c08826SMartin Kepplinger 	if (ret)
1423e8c08826SMartin Kepplinger 		return ret;
1424e8c08826SMartin Kepplinger 
1425e8c08826SMartin Kepplinger 	ctrl_hdlr->lock = &hi846->mutex;
1426e8c08826SMartin Kepplinger 
1427e8c08826SMartin Kepplinger 	hi846->link_freq =
1428e8c08826SMartin Kepplinger 		v4l2_ctrl_new_int_menu(ctrl_hdlr, &hi846_ctrl_ops,
1429e8c08826SMartin Kepplinger 				       V4L2_CID_LINK_FREQ,
1430e8c08826SMartin Kepplinger 				       ARRAY_SIZE(hi846_link_freqs) - 1,
1431e8c08826SMartin Kepplinger 				       0, hi846_link_freqs);
1432e8c08826SMartin Kepplinger 	if (hi846->link_freq)
1433e8c08826SMartin Kepplinger 		hi846->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1434e8c08826SMartin Kepplinger 
1435e8c08826SMartin Kepplinger 	hi846->pixel_rate =
1436e8c08826SMartin Kepplinger 		v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops,
1437e8c08826SMartin Kepplinger 				  V4L2_CID_PIXEL_RATE, 0,
1438e8c08826SMartin Kepplinger 				  hi846_calc_pixel_rate(hi846), 1,
1439e8c08826SMartin Kepplinger 				  hi846_calc_pixel_rate(hi846));
1440e8c08826SMartin Kepplinger 	hi846->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops,
1441e8c08826SMartin Kepplinger 					  V4L2_CID_VBLANK,
1442e8c08826SMartin Kepplinger 					  hi846->cur_mode->frame_len -
1443e8c08826SMartin Kepplinger 					  hi846->cur_mode->height,
1444e8c08826SMartin Kepplinger 					  HI846_FLL_MAX -
1445e8c08826SMartin Kepplinger 					  hi846->cur_mode->height, 1,
1446e8c08826SMartin Kepplinger 					  hi846->cur_mode->frame_len -
1447e8c08826SMartin Kepplinger 					  hi846->cur_mode->height);
1448e8c08826SMartin Kepplinger 
1449e8c08826SMartin Kepplinger 	h_blank = hi846->cur_mode->llp - hi846->cur_mode->width;
1450e8c08826SMartin Kepplinger 
1451e8c08826SMartin Kepplinger 	hi846->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops,
1452e8c08826SMartin Kepplinger 					  V4L2_CID_HBLANK, h_blank, h_blank, 1,
1453e8c08826SMartin Kepplinger 					  h_blank);
1454e8c08826SMartin Kepplinger 	if (hi846->hblank)
1455e8c08826SMartin Kepplinger 		hi846->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1456e8c08826SMartin Kepplinger 
1457e8c08826SMartin Kepplinger 	v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
1458e8c08826SMartin Kepplinger 			  HI846_ANAL_GAIN_MIN, HI846_ANAL_GAIN_MAX,
1459e8c08826SMartin Kepplinger 			  HI846_ANAL_GAIN_STEP, HI846_ANAL_GAIN_MIN);
1460e8c08826SMartin Kepplinger 	v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
1461e8c08826SMartin Kepplinger 			  HI846_DGTL_GAIN_MIN, HI846_DGTL_GAIN_MAX,
1462e8c08826SMartin Kepplinger 			  HI846_DGTL_GAIN_STEP, HI846_DGTL_GAIN_DEFAULT);
1463e8c08826SMartin Kepplinger 	exposure_max = hi846->cur_mode->frame_len - HI846_EXPOSURE_MAX_MARGIN;
1464e8c08826SMartin Kepplinger 	hi846->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hi846_ctrl_ops,
1465e8c08826SMartin Kepplinger 					    V4L2_CID_EXPOSURE,
1466e8c08826SMartin Kepplinger 					    HI846_EXPOSURE_MIN, exposure_max,
1467e8c08826SMartin Kepplinger 					    HI846_EXPOSURE_STEP,
1468e8c08826SMartin Kepplinger 					    exposure_max);
1469e8c08826SMartin Kepplinger 	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hi846_ctrl_ops,
1470e8c08826SMartin Kepplinger 				     V4L2_CID_TEST_PATTERN,
1471e8c08826SMartin Kepplinger 				     ARRAY_SIZE(hi846_test_pattern_menu) - 1,
1472e8c08826SMartin Kepplinger 				     0, 0, hi846_test_pattern_menu);
1473e8c08826SMartin Kepplinger 	if (ctrl_hdlr->error) {
1474e8c08826SMartin Kepplinger 		dev_err(&client->dev, "v4l ctrl handler error: %d\n",
1475e8c08826SMartin Kepplinger 			ctrl_hdlr->error);
14762649c1a2SWei Chen 		ret = ctrl_hdlr->error;
14772649c1a2SWei Chen 		goto error;
1478e8c08826SMartin Kepplinger 	}
1479e8c08826SMartin Kepplinger 
1480e8c08826SMartin Kepplinger 	ret = v4l2_fwnode_device_parse(&client->dev, &props);
1481e8c08826SMartin Kepplinger 	if (ret)
14822649c1a2SWei Chen 		goto error;
1483e8c08826SMartin Kepplinger 
1484e8c08826SMartin Kepplinger 	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hi846_ctrl_ops,
1485e8c08826SMartin Kepplinger 					      &props);
1486e8c08826SMartin Kepplinger 	if (ret)
14872649c1a2SWei Chen 		goto error;
1488e8c08826SMartin Kepplinger 
1489e8c08826SMartin Kepplinger 	hi846->sd.ctrl_handler = ctrl_hdlr;
1490e8c08826SMartin Kepplinger 
1491e8c08826SMartin Kepplinger 	return 0;
14922649c1a2SWei Chen 
14932649c1a2SWei Chen error:
14942649c1a2SWei Chen 	v4l2_ctrl_handler_free(ctrl_hdlr);
14952649c1a2SWei Chen 	return ret;
1496e8c08826SMartin Kepplinger }
1497e8c08826SMartin Kepplinger 
hi846_set_video_mode(struct hi846 * hi846,int fps)1498e8c08826SMartin Kepplinger static int hi846_set_video_mode(struct hi846 *hi846, int fps)
1499e8c08826SMartin Kepplinger {
1500e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1501e8c08826SMartin Kepplinger 	u64 frame_length;
1502e8c08826SMartin Kepplinger 	int ret = 0;
1503e8c08826SMartin Kepplinger 	int dummy_lines;
1504e8c08826SMartin Kepplinger 	u64 link_freq = hi846_get_link_freq(hi846);
1505e8c08826SMartin Kepplinger 
1506e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: link freq: %llu\n", __func__,
1507e8c08826SMartin Kepplinger 		hi846_get_link_freq(hi846));
1508e8c08826SMartin Kepplinger 
1509e8c08826SMartin Kepplinger 	do_div(link_freq, fps);
1510e8c08826SMartin Kepplinger 	frame_length = link_freq;
1511e8c08826SMartin Kepplinger 	do_div(frame_length, HI846_LINE_LENGTH);
1512e8c08826SMartin Kepplinger 
1513e8c08826SMartin Kepplinger 	dummy_lines = (frame_length > hi846->cur_mode->frame_len) ?
1514e8c08826SMartin Kepplinger 			(frame_length - hi846->cur_mode->frame_len) : 0;
1515e8c08826SMartin Kepplinger 
1516e8c08826SMartin Kepplinger 	frame_length = hi846->cur_mode->frame_len + dummy_lines;
1517e8c08826SMartin Kepplinger 
1518e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: frame length calculated: %llu\n", __func__,
1519e8c08826SMartin Kepplinger 		frame_length);
1520e8c08826SMartin Kepplinger 
1521e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_FLL, frame_length & 0xFFFF, &ret);
1522e8c08826SMartin Kepplinger 	hi846_write_reg_16(hi846, HI846_REG_LLP,
1523e8c08826SMartin Kepplinger 			   HI846_LINE_LENGTH & 0xFFFF, &ret);
1524e8c08826SMartin Kepplinger 
1525e8c08826SMartin Kepplinger 	return ret;
1526e8c08826SMartin Kepplinger }
1527e8c08826SMartin Kepplinger 
hi846_start_streaming(struct hi846 * hi846)1528e8c08826SMartin Kepplinger static int hi846_start_streaming(struct hi846 *hi846)
1529e8c08826SMartin Kepplinger {
1530e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1531e8c08826SMartin Kepplinger 	int ret = 0;
1532e8c08826SMartin Kepplinger 	u8 val;
1533e8c08826SMartin Kepplinger 
1534e8c08826SMartin Kepplinger 	if (hi846->nr_lanes == 2)
1535e8c08826SMartin Kepplinger 		ret = hi846_write_reg_list(hi846, &hi846_init_regs_list_2lane);
1536e8c08826SMartin Kepplinger 	else
1537e8c08826SMartin Kepplinger 		ret = hi846_write_reg_list(hi846, &hi846_init_regs_list_4lane);
1538e8c08826SMartin Kepplinger 	if (ret) {
1539e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to set plls: %d\n", ret);
1540e8c08826SMartin Kepplinger 		return ret;
1541e8c08826SMartin Kepplinger 	}
1542e8c08826SMartin Kepplinger 
1543e8c08826SMartin Kepplinger 	ret = hi846_write_reg_list(hi846, &hi846->cur_mode->reg_list_config);
1544e8c08826SMartin Kepplinger 	if (ret) {
1545e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to set mode: %d\n", ret);
1546e8c08826SMartin Kepplinger 		return ret;
1547e8c08826SMartin Kepplinger 	}
1548e8c08826SMartin Kepplinger 
1549e8c08826SMartin Kepplinger 	if (hi846->nr_lanes == 2)
1550e8c08826SMartin Kepplinger 		ret = hi846_write_reg_list(hi846,
1551e8c08826SMartin Kepplinger 					   &hi846->cur_mode->reg_list_2lane);
1552e8c08826SMartin Kepplinger 	else
1553e8c08826SMartin Kepplinger 		ret = hi846_write_reg_list(hi846,
1554e8c08826SMartin Kepplinger 					   &hi846->cur_mode->reg_list_4lane);
1555e8c08826SMartin Kepplinger 	if (ret) {
1556e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to set mipi mode: %d\n", ret);
1557e8c08826SMartin Kepplinger 		return ret;
1558e8c08826SMartin Kepplinger 	}
1559e8c08826SMartin Kepplinger 
1560e8c08826SMartin Kepplinger 	hi846_set_video_mode(hi846, hi846->cur_mode->fps);
1561e8c08826SMartin Kepplinger 
1562e8c08826SMartin Kepplinger 	ret = __v4l2_ctrl_handler_setup(hi846->sd.ctrl_handler);
1563e8c08826SMartin Kepplinger 	if (ret)
1564e8c08826SMartin Kepplinger 		return ret;
1565e8c08826SMartin Kepplinger 
1566e8c08826SMartin Kepplinger 	/*
1567e8c08826SMartin Kepplinger 	 * Reading 0x0034 is purely done for debugging reasons: It is not
1568e8c08826SMartin Kepplinger 	 * documented in the DS but only mentioned once:
1569e8c08826SMartin Kepplinger 	 * "If 0x0034[2] bit is disabled , Visible pixel width and height is 0."
1570e8c08826SMartin Kepplinger 	 * So even though that sounds like we won't see anything, we don't
1571e8c08826SMartin Kepplinger 	 * know more about this, so in that case only inform the user but do
1572e8c08826SMartin Kepplinger 	 * nothing more.
1573e8c08826SMartin Kepplinger 	 */
1574e8c08826SMartin Kepplinger 	ret = hi846_read_reg(hi846, 0x0034, &val);
1575e8c08826SMartin Kepplinger 	if (ret)
1576e8c08826SMartin Kepplinger 		return ret;
1577e8c08826SMartin Kepplinger 	if (!(val & BIT(2)))
1578e8c08826SMartin Kepplinger 		dev_info(&client->dev, "visible pixel width and height is 0\n");
1579e8c08826SMartin Kepplinger 
1580e8c08826SMartin Kepplinger 	ret = hi846_write_reg(hi846, HI846_REG_MODE_SELECT,
1581e8c08826SMartin Kepplinger 			      HI846_MODE_STREAMING);
1582e8c08826SMartin Kepplinger 	if (ret) {
1583e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to start stream");
1584e8c08826SMartin Kepplinger 		return ret;
1585e8c08826SMartin Kepplinger 	}
1586e8c08826SMartin Kepplinger 
1587e8c08826SMartin Kepplinger 	hi846->streaming = 1;
1588e8c08826SMartin Kepplinger 
1589e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: started streaming successfully\n", __func__);
1590e8c08826SMartin Kepplinger 
1591e8c08826SMartin Kepplinger 	return ret;
1592e8c08826SMartin Kepplinger }
1593e8c08826SMartin Kepplinger 
hi846_stop_streaming(struct hi846 * hi846)1594e8c08826SMartin Kepplinger static void hi846_stop_streaming(struct hi846 *hi846)
1595e8c08826SMartin Kepplinger {
1596e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1597e8c08826SMartin Kepplinger 
1598e8c08826SMartin Kepplinger 	if (hi846_write_reg(hi846, HI846_REG_MODE_SELECT, HI846_MODE_STANDBY))
1599e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to stop stream");
1600e8c08826SMartin Kepplinger 
1601e8c08826SMartin Kepplinger 	hi846->streaming = 0;
1602e8c08826SMartin Kepplinger }
1603e8c08826SMartin Kepplinger 
hi846_set_stream(struct v4l2_subdev * sd,int enable)1604e8c08826SMartin Kepplinger static int hi846_set_stream(struct v4l2_subdev *sd, int enable)
1605e8c08826SMartin Kepplinger {
1606e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1607e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1608e8c08826SMartin Kepplinger 	int ret = 0;
1609e8c08826SMartin Kepplinger 
1610e8c08826SMartin Kepplinger 	if (hi846->streaming == enable)
1611e8c08826SMartin Kepplinger 		return 0;
1612e8c08826SMartin Kepplinger 
1613e8c08826SMartin Kepplinger 	mutex_lock(&hi846->mutex);
1614e8c08826SMartin Kepplinger 
1615e8c08826SMartin Kepplinger 	if (enable) {
1616e8c08826SMartin Kepplinger 		ret = pm_runtime_get_sync(&client->dev);
1617e8c08826SMartin Kepplinger 		if (ret < 0) {
1618e8c08826SMartin Kepplinger 			pm_runtime_put_noidle(&client->dev);
1619e8c08826SMartin Kepplinger 			goto out;
1620e8c08826SMartin Kepplinger 		}
1621e8c08826SMartin Kepplinger 
1622e8c08826SMartin Kepplinger 		ret = hi846_start_streaming(hi846);
1623e8c08826SMartin Kepplinger 	}
1624e8c08826SMartin Kepplinger 
1625e8c08826SMartin Kepplinger 	if (!enable || ret) {
1626e8c08826SMartin Kepplinger 		hi846_stop_streaming(hi846);
1627e8c08826SMartin Kepplinger 		pm_runtime_put(&client->dev);
1628e8c08826SMartin Kepplinger 	}
1629e8c08826SMartin Kepplinger 
1630e8c08826SMartin Kepplinger out:
1631e8c08826SMartin Kepplinger 	mutex_unlock(&hi846->mutex);
1632e8c08826SMartin Kepplinger 
1633e8c08826SMartin Kepplinger 	return ret;
1634e8c08826SMartin Kepplinger }
1635e8c08826SMartin Kepplinger 
hi846_power_on(struct hi846 * hi846)1636e8c08826SMartin Kepplinger static int hi846_power_on(struct hi846 *hi846)
1637e8c08826SMartin Kepplinger {
1638e8c08826SMartin Kepplinger 	int ret;
1639e8c08826SMartin Kepplinger 
1640e8c08826SMartin Kepplinger 	ret = regulator_bulk_enable(HI846_NUM_SUPPLIES, hi846->supplies);
1641e8c08826SMartin Kepplinger 	if (ret < 0)
1642e8c08826SMartin Kepplinger 		return ret;
1643e8c08826SMartin Kepplinger 
1644e8c08826SMartin Kepplinger 	ret = clk_prepare_enable(hi846->clock);
1645e8c08826SMartin Kepplinger 	if (ret < 0)
1646e8c08826SMartin Kepplinger 		goto err_reg;
1647e8c08826SMartin Kepplinger 
1648e8c08826SMartin Kepplinger 	if (hi846->shutdown_gpio)
1649e8c08826SMartin Kepplinger 		gpiod_set_value_cansleep(hi846->shutdown_gpio, 0);
1650e8c08826SMartin Kepplinger 
1651e8c08826SMartin Kepplinger 	/* 30us = 2400 cycles at 80Mhz */
1652e8c08826SMartin Kepplinger 	usleep_range(30, 60);
1653e8c08826SMartin Kepplinger 	if (hi846->rst_gpio)
1654e8c08826SMartin Kepplinger 		gpiod_set_value_cansleep(hi846->rst_gpio, 0);
1655e8c08826SMartin Kepplinger 	usleep_range(30, 60);
1656e8c08826SMartin Kepplinger 
1657e8c08826SMartin Kepplinger 	return 0;
1658e8c08826SMartin Kepplinger 
1659e8c08826SMartin Kepplinger err_reg:
1660e8c08826SMartin Kepplinger 	regulator_bulk_disable(HI846_NUM_SUPPLIES, hi846->supplies);
1661e8c08826SMartin Kepplinger 
1662e8c08826SMartin Kepplinger 	return ret;
1663e8c08826SMartin Kepplinger }
1664e8c08826SMartin Kepplinger 
hi846_power_off(struct hi846 * hi846)1665e1cc0a05SMartin Kepplinger static int hi846_power_off(struct hi846 *hi846)
1666e8c08826SMartin Kepplinger {
1667e8c08826SMartin Kepplinger 	if (hi846->rst_gpio)
1668e8c08826SMartin Kepplinger 		gpiod_set_value_cansleep(hi846->rst_gpio, 1);
1669e8c08826SMartin Kepplinger 
1670e8c08826SMartin Kepplinger 	if (hi846->shutdown_gpio)
1671e8c08826SMartin Kepplinger 		gpiod_set_value_cansleep(hi846->shutdown_gpio, 1);
1672e8c08826SMartin Kepplinger 
1673e8c08826SMartin Kepplinger 	clk_disable_unprepare(hi846->clock);
1674e1cc0a05SMartin Kepplinger 	return regulator_bulk_disable(HI846_NUM_SUPPLIES, hi846->supplies);
1675e8c08826SMartin Kepplinger }
1676e8c08826SMartin Kepplinger 
hi846_suspend(struct device * dev)1677e8c08826SMartin Kepplinger static int __maybe_unused hi846_suspend(struct device *dev)
1678e8c08826SMartin Kepplinger {
1679e8c08826SMartin Kepplinger 	struct i2c_client *client = to_i2c_client(dev);
1680e8c08826SMartin Kepplinger 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1681e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1682e8c08826SMartin Kepplinger 
1683e8c08826SMartin Kepplinger 	if (hi846->streaming)
1684e8c08826SMartin Kepplinger 		hi846_stop_streaming(hi846);
1685e8c08826SMartin Kepplinger 
1686e1cc0a05SMartin Kepplinger 	return hi846_power_off(hi846);
1687e8c08826SMartin Kepplinger }
1688e8c08826SMartin Kepplinger 
hi846_resume(struct device * dev)1689e8c08826SMartin Kepplinger static int __maybe_unused hi846_resume(struct device *dev)
1690e8c08826SMartin Kepplinger {
1691e8c08826SMartin Kepplinger 	struct i2c_client *client = to_i2c_client(dev);
1692e8c08826SMartin Kepplinger 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1693e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1694e8c08826SMartin Kepplinger 	int ret;
1695e8c08826SMartin Kepplinger 
1696e8c08826SMartin Kepplinger 	ret = hi846_power_on(hi846);
1697e8c08826SMartin Kepplinger 	if (ret)
1698e8c08826SMartin Kepplinger 		return ret;
1699e8c08826SMartin Kepplinger 
1700e8c08826SMartin Kepplinger 	if (hi846->streaming) {
1701e8c08826SMartin Kepplinger 		ret = hi846_start_streaming(hi846);
1702e8c08826SMartin Kepplinger 		if (ret) {
1703e8c08826SMartin Kepplinger 			dev_err(dev, "%s: start streaming failed: %d\n",
1704e8c08826SMartin Kepplinger 				__func__, ret);
1705e8c08826SMartin Kepplinger 			goto error;
1706e8c08826SMartin Kepplinger 		}
1707e8c08826SMartin Kepplinger 	}
1708e8c08826SMartin Kepplinger 
1709e8c08826SMartin Kepplinger 	return 0;
1710e8c08826SMartin Kepplinger 
1711e8c08826SMartin Kepplinger error:
1712e8c08826SMartin Kepplinger 	hi846_power_off(hi846);
1713e8c08826SMartin Kepplinger 	return ret;
1714e8c08826SMartin Kepplinger }
1715e8c08826SMartin Kepplinger 
hi846_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)1716e8c08826SMartin Kepplinger static int hi846_set_format(struct v4l2_subdev *sd,
1717e8c08826SMartin Kepplinger 			    struct v4l2_subdev_state *sd_state,
1718e8c08826SMartin Kepplinger 			    struct v4l2_subdev_format *format)
1719e8c08826SMartin Kepplinger {
1720e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1721e8c08826SMartin Kepplinger 	struct v4l2_mbus_framefmt *mf = &format->format;
1722e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1723e8c08826SMartin Kepplinger 	const struct hi846_datafmt *fmt = hi846_find_datafmt(mf->code);
1724e8c08826SMartin Kepplinger 	u32 tgt_fps;
1725e8c08826SMartin Kepplinger 	s32 vblank_def, h_blank;
1726e8c08826SMartin Kepplinger 
1727e8c08826SMartin Kepplinger 	if (!fmt) {
1728e8c08826SMartin Kepplinger 		mf->code = hi846_colour_fmts[0].code;
1729e8c08826SMartin Kepplinger 		mf->colorspace = hi846_colour_fmts[0].colorspace;
1730e8c08826SMartin Kepplinger 		fmt = &hi846_colour_fmts[0];
1731e8c08826SMartin Kepplinger 	}
1732e8c08826SMartin Kepplinger 
1733e8c08826SMartin Kepplinger 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1734e8c08826SMartin Kepplinger 		*v4l2_subdev_get_try_format(sd, sd_state, format->pad) = *mf;
1735e8c08826SMartin Kepplinger 		return 0;
1736e8c08826SMartin Kepplinger 	}
1737e8c08826SMartin Kepplinger 
1738e8c08826SMartin Kepplinger 	if (hi846->nr_lanes == 2) {
1739e8c08826SMartin Kepplinger 		if (!hi846->cur_mode->reg_list_2lane.num_of_regs) {
1740e8c08826SMartin Kepplinger 			dev_err(&client->dev,
1741e8c08826SMartin Kepplinger 				"this mode is not supported for 2 lanes\n");
1742e8c08826SMartin Kepplinger 			return -EINVAL;
1743e8c08826SMartin Kepplinger 		}
1744e8c08826SMartin Kepplinger 	} else {
1745e8c08826SMartin Kepplinger 		if (!hi846->cur_mode->reg_list_4lane.num_of_regs) {
1746e8c08826SMartin Kepplinger 			dev_err(&client->dev,
1747e8c08826SMartin Kepplinger 				"this mode is not supported for 4 lanes\n");
1748e8c08826SMartin Kepplinger 			return -EINVAL;
1749e8c08826SMartin Kepplinger 		}
1750e8c08826SMartin Kepplinger 	}
1751e8c08826SMartin Kepplinger 
1752e8c08826SMartin Kepplinger 	mutex_lock(&hi846->mutex);
1753e8c08826SMartin Kepplinger 
1754e8c08826SMartin Kepplinger 	if (hi846->streaming) {
1755e8c08826SMartin Kepplinger 		mutex_unlock(&hi846->mutex);
1756e8c08826SMartin Kepplinger 		return -EBUSY;
1757e8c08826SMartin Kepplinger 	}
1758e8c08826SMartin Kepplinger 
1759e8c08826SMartin Kepplinger 	hi846->fmt = fmt;
1760e8c08826SMartin Kepplinger 
1761e8c08826SMartin Kepplinger 	hi846->cur_mode =
1762e8c08826SMartin Kepplinger 		v4l2_find_nearest_size(supported_modes,
1763e8c08826SMartin Kepplinger 				       ARRAY_SIZE(supported_modes),
1764e8c08826SMartin Kepplinger 				       width, height, mf->width, mf->height);
1765e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: found mode: %dx%d\n", __func__,
1766e8c08826SMartin Kepplinger 		hi846->cur_mode->width, hi846->cur_mode->height);
1767e8c08826SMartin Kepplinger 
1768e8c08826SMartin Kepplinger 	tgt_fps = hi846->cur_mode->fps;
1769e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: target fps: %d\n", __func__, tgt_fps);
1770e8c08826SMartin Kepplinger 
1771e8c08826SMartin Kepplinger 	mf->width = hi846->cur_mode->width;
1772e8c08826SMartin Kepplinger 	mf->height = hi846->cur_mode->height;
1773e8c08826SMartin Kepplinger 	mf->code = HI846_MEDIA_BUS_FORMAT;
1774e8c08826SMartin Kepplinger 	mf->field = V4L2_FIELD_NONE;
1775e8c08826SMartin Kepplinger 
1776e8c08826SMartin Kepplinger 	__v4l2_ctrl_s_ctrl(hi846->link_freq, hi846_get_link_freq_index(hi846));
1777e8c08826SMartin Kepplinger 	__v4l2_ctrl_s_ctrl_int64(hi846->pixel_rate,
1778e8c08826SMartin Kepplinger 				 hi846_calc_pixel_rate(hi846));
1779e8c08826SMartin Kepplinger 
1780e8c08826SMartin Kepplinger 	/* Update limits and set FPS to default */
1781e8c08826SMartin Kepplinger 	vblank_def = hi846->cur_mode->frame_len - hi846->cur_mode->height;
1782e8c08826SMartin Kepplinger 	__v4l2_ctrl_modify_range(hi846->vblank,
1783e8c08826SMartin Kepplinger 				 hi846->cur_mode->frame_len -
1784e8c08826SMartin Kepplinger 					hi846->cur_mode->height,
1785e8c08826SMartin Kepplinger 				 HI846_FLL_MAX - hi846->cur_mode->height, 1,
1786e8c08826SMartin Kepplinger 				 vblank_def);
1787e8c08826SMartin Kepplinger 	__v4l2_ctrl_s_ctrl(hi846->vblank, vblank_def);
1788e8c08826SMartin Kepplinger 
1789e8c08826SMartin Kepplinger 	h_blank = hi846->cur_mode->llp - hi846->cur_mode->width;
1790e8c08826SMartin Kepplinger 
1791e8c08826SMartin Kepplinger 	__v4l2_ctrl_modify_range(hi846->hblank, h_blank, h_blank, 1,
1792e8c08826SMartin Kepplinger 				 h_blank);
1793e8c08826SMartin Kepplinger 
1794e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "Set fmt w=%d h=%d code=0x%x colorspace=0x%x\n",
1795e8c08826SMartin Kepplinger 		mf->width, mf->height,
1796e8c08826SMartin Kepplinger 		fmt->code, fmt->colorspace);
1797e8c08826SMartin Kepplinger 
1798e8c08826SMartin Kepplinger 	mutex_unlock(&hi846->mutex);
1799e8c08826SMartin Kepplinger 
1800e8c08826SMartin Kepplinger 	return 0;
1801e8c08826SMartin Kepplinger }
1802e8c08826SMartin Kepplinger 
hi846_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)1803e8c08826SMartin Kepplinger static int hi846_get_format(struct v4l2_subdev *sd,
1804e8c08826SMartin Kepplinger 			    struct v4l2_subdev_state *sd_state,
1805e8c08826SMartin Kepplinger 			    struct v4l2_subdev_format *format)
1806e8c08826SMartin Kepplinger {
1807e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1808e8c08826SMartin Kepplinger 	struct v4l2_mbus_framefmt *mf = &format->format;
1809e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1810e8c08826SMartin Kepplinger 
1811e8c08826SMartin Kepplinger 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1812e8c08826SMartin Kepplinger 		format->format = *v4l2_subdev_get_try_format(&hi846->sd,
1813e8c08826SMartin Kepplinger 							sd_state,
1814e8c08826SMartin Kepplinger 							format->pad);
1815e8c08826SMartin Kepplinger 		return 0;
1816e8c08826SMartin Kepplinger 	}
1817e8c08826SMartin Kepplinger 
1818e8c08826SMartin Kepplinger 	mutex_lock(&hi846->mutex);
1819e8c08826SMartin Kepplinger 	mf->code        = HI846_MEDIA_BUS_FORMAT;
1820e8c08826SMartin Kepplinger 	mf->colorspace  = V4L2_COLORSPACE_RAW;
1821e8c08826SMartin Kepplinger 	mf->field       = V4L2_FIELD_NONE;
1822e8c08826SMartin Kepplinger 	mf->width       = hi846->cur_mode->width;
1823e8c08826SMartin Kepplinger 	mf->height      = hi846->cur_mode->height;
1824e8c08826SMartin Kepplinger 	mutex_unlock(&hi846->mutex);
1825e8c08826SMartin Kepplinger 	dev_dbg(&client->dev,
1826e8c08826SMartin Kepplinger 		"Get format w=%d h=%d code=0x%x colorspace=0x%x\n",
1827e8c08826SMartin Kepplinger 		mf->width, mf->height, mf->code, mf->colorspace);
1828e8c08826SMartin Kepplinger 
1829e8c08826SMartin Kepplinger 	return 0;
1830e8c08826SMartin Kepplinger }
1831e8c08826SMartin Kepplinger 
hi846_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)1832e8c08826SMartin Kepplinger static int hi846_enum_mbus_code(struct v4l2_subdev *sd,
1833e8c08826SMartin Kepplinger 				struct v4l2_subdev_state *sd_state,
1834e8c08826SMartin Kepplinger 				struct v4l2_subdev_mbus_code_enum *code)
1835e8c08826SMartin Kepplinger {
1836e8c08826SMartin Kepplinger 	if (code->pad || code->index > 0)
1837e8c08826SMartin Kepplinger 		return -EINVAL;
1838e8c08826SMartin Kepplinger 
1839e8c08826SMartin Kepplinger 	code->code = HI846_MEDIA_BUS_FORMAT;
1840e8c08826SMartin Kepplinger 
1841e8c08826SMartin Kepplinger 	return 0;
1842e8c08826SMartin Kepplinger }
1843e8c08826SMartin Kepplinger 
hi846_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)1844e8c08826SMartin Kepplinger static int hi846_enum_frame_size(struct v4l2_subdev *sd,
1845e8c08826SMartin Kepplinger 				 struct v4l2_subdev_state *sd_state,
1846e8c08826SMartin Kepplinger 				 struct v4l2_subdev_frame_size_enum *fse)
1847e8c08826SMartin Kepplinger {
1848e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1849e8c08826SMartin Kepplinger 
1850e8c08826SMartin Kepplinger 	if (fse->pad || fse->index >= ARRAY_SIZE(supported_modes))
1851e8c08826SMartin Kepplinger 		return -EINVAL;
1852e8c08826SMartin Kepplinger 
1853e8c08826SMartin Kepplinger 	if (fse->code != HI846_MEDIA_BUS_FORMAT) {
1854e8c08826SMartin Kepplinger 		dev_err(&client->dev, "frame size enum not matching\n");
1855e8c08826SMartin Kepplinger 		return -EINVAL;
1856e8c08826SMartin Kepplinger 	}
1857e8c08826SMartin Kepplinger 
1858e8c08826SMartin Kepplinger 	fse->min_width = supported_modes[fse->index].width;
1859e8c08826SMartin Kepplinger 	fse->max_width = supported_modes[fse->index].width;
1860e8c08826SMartin Kepplinger 	fse->min_height = supported_modes[fse->index].height;
1861e8c08826SMartin Kepplinger 	fse->max_height = supported_modes[fse->index].height;
1862e8c08826SMartin Kepplinger 
1863e8c08826SMartin Kepplinger 	dev_dbg(&client->dev, "%s: max width: %d max height: %d\n", __func__,
1864e8c08826SMartin Kepplinger 		fse->max_width, fse->max_height);
1865e8c08826SMartin Kepplinger 
1866e8c08826SMartin Kepplinger 	return 0;
1867e8c08826SMartin Kepplinger }
1868e8c08826SMartin Kepplinger 
hi846_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)1869e8c08826SMartin Kepplinger static int hi846_get_selection(struct v4l2_subdev *sd,
1870e8c08826SMartin Kepplinger 			       struct v4l2_subdev_state *sd_state,
1871e8c08826SMartin Kepplinger 			       struct v4l2_subdev_selection *sel)
1872e8c08826SMartin Kepplinger {
1873e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1874e8c08826SMartin Kepplinger 
1875e8c08826SMartin Kepplinger 	switch (sel->target) {
1876e8c08826SMartin Kepplinger 	case V4L2_SEL_TGT_CROP:
1877e8c08826SMartin Kepplinger 	case V4L2_SEL_TGT_CROP_DEFAULT:
1878e8c08826SMartin Kepplinger 		mutex_lock(&hi846->mutex);
1879e8c08826SMartin Kepplinger 		switch (sel->which) {
1880e8c08826SMartin Kepplinger 		case V4L2_SUBDEV_FORMAT_TRY:
1881e8c08826SMartin Kepplinger 			v4l2_subdev_get_try_crop(sd, sd_state, sel->pad);
1882e8c08826SMartin Kepplinger 			break;
1883e8c08826SMartin Kepplinger 		case V4L2_SUBDEV_FORMAT_ACTIVE:
1884e8c08826SMartin Kepplinger 			sel->r = hi846->cur_mode->crop;
1885e8c08826SMartin Kepplinger 			break;
1886e8c08826SMartin Kepplinger 		}
1887e8c08826SMartin Kepplinger 		mutex_unlock(&hi846->mutex);
1888e8c08826SMartin Kepplinger 		return 0;
1889e8c08826SMartin Kepplinger 	case V4L2_SEL_TGT_CROP_BOUNDS:
1890e8c08826SMartin Kepplinger 	case V4L2_SEL_TGT_NATIVE_SIZE:
1891e8c08826SMartin Kepplinger 		sel->r.top = 0;
1892e8c08826SMartin Kepplinger 		sel->r.left = 0;
1893e8c08826SMartin Kepplinger 		sel->r.width = 3264;
1894e8c08826SMartin Kepplinger 		sel->r.height = 2448;
1895e8c08826SMartin Kepplinger 		return 0;
1896e8c08826SMartin Kepplinger 	default:
1897e8c08826SMartin Kepplinger 		return -EINVAL;
1898e8c08826SMartin Kepplinger 	}
1899e8c08826SMartin Kepplinger }
1900e8c08826SMartin Kepplinger 
hi846_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)1901e8c08826SMartin Kepplinger static int hi846_init_cfg(struct v4l2_subdev *sd,
1902e8c08826SMartin Kepplinger 			  struct v4l2_subdev_state *sd_state)
1903e8c08826SMartin Kepplinger {
1904e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
1905e8c08826SMartin Kepplinger 	struct v4l2_mbus_framefmt *mf;
1906e8c08826SMartin Kepplinger 
1907e8c08826SMartin Kepplinger 	mf = v4l2_subdev_get_try_format(sd, sd_state, 0);
1908e8c08826SMartin Kepplinger 
1909e8c08826SMartin Kepplinger 	mutex_lock(&hi846->mutex);
1910e8c08826SMartin Kepplinger 	mf->code        = HI846_MEDIA_BUS_FORMAT;
1911e8c08826SMartin Kepplinger 	mf->colorspace  = V4L2_COLORSPACE_RAW;
1912e8c08826SMartin Kepplinger 	mf->field       = V4L2_FIELD_NONE;
1913e8c08826SMartin Kepplinger 	mf->width       = hi846->cur_mode->width;
1914e8c08826SMartin Kepplinger 	mf->height      = hi846->cur_mode->height;
1915e8c08826SMartin Kepplinger 	mutex_unlock(&hi846->mutex);
1916e8c08826SMartin Kepplinger 
1917e8c08826SMartin Kepplinger 	return 0;
1918e8c08826SMartin Kepplinger }
1919e8c08826SMartin Kepplinger 
1920e8c08826SMartin Kepplinger static const struct v4l2_subdev_video_ops hi846_video_ops = {
1921e8c08826SMartin Kepplinger 	.s_stream = hi846_set_stream,
1922e8c08826SMartin Kepplinger };
1923e8c08826SMartin Kepplinger 
1924e8c08826SMartin Kepplinger static const struct v4l2_subdev_pad_ops hi846_pad_ops = {
1925e8c08826SMartin Kepplinger 	.init_cfg = hi846_init_cfg,
1926e8c08826SMartin Kepplinger 	.enum_frame_size = hi846_enum_frame_size,
1927e8c08826SMartin Kepplinger 	.enum_mbus_code = hi846_enum_mbus_code,
1928e8c08826SMartin Kepplinger 	.set_fmt = hi846_set_format,
1929e8c08826SMartin Kepplinger 	.get_fmt = hi846_get_format,
1930e8c08826SMartin Kepplinger 	.get_selection = hi846_get_selection,
1931e8c08826SMartin Kepplinger };
1932e8c08826SMartin Kepplinger 
1933e8c08826SMartin Kepplinger static const struct v4l2_subdev_ops hi846_subdev_ops = {
1934e8c08826SMartin Kepplinger 	.video = &hi846_video_ops,
1935e8c08826SMartin Kepplinger 	.pad = &hi846_pad_ops,
1936e8c08826SMartin Kepplinger };
1937e8c08826SMartin Kepplinger 
1938e8c08826SMartin Kepplinger static const struct media_entity_operations hi846_subdev_entity_ops = {
1939e8c08826SMartin Kepplinger 	.link_validate = v4l2_subdev_link_validate,
1940e8c08826SMartin Kepplinger };
1941e8c08826SMartin Kepplinger 
hi846_identify_module(struct hi846 * hi846)1942e8c08826SMartin Kepplinger static int hi846_identify_module(struct hi846 *hi846)
1943e8c08826SMartin Kepplinger {
1944e8c08826SMartin Kepplinger 	struct i2c_client *client = v4l2_get_subdevdata(&hi846->sd);
1945e8c08826SMartin Kepplinger 	int ret;
1946e8c08826SMartin Kepplinger 	u8 hi, lo;
1947e8c08826SMartin Kepplinger 
1948e8c08826SMartin Kepplinger 	ret = hi846_read_reg(hi846, HI846_REG_CHIP_ID_L, &lo);
1949e8c08826SMartin Kepplinger 	if (ret)
1950e8c08826SMartin Kepplinger 		return ret;
1951e8c08826SMartin Kepplinger 
1952e8c08826SMartin Kepplinger 	if (lo != HI846_CHIP_ID_L) {
1953e8c08826SMartin Kepplinger 		dev_err(&client->dev, "wrong chip id low byte: %x", lo);
1954e8c08826SMartin Kepplinger 		return -ENXIO;
1955e8c08826SMartin Kepplinger 	}
1956e8c08826SMartin Kepplinger 
1957e8c08826SMartin Kepplinger 	ret = hi846_read_reg(hi846, HI846_REG_CHIP_ID_H, &hi);
1958e8c08826SMartin Kepplinger 	if (ret)
1959e8c08826SMartin Kepplinger 		return ret;
1960e8c08826SMartin Kepplinger 
1961e8c08826SMartin Kepplinger 	if (hi != HI846_CHIP_ID_H) {
1962e8c08826SMartin Kepplinger 		dev_err(&client->dev, "wrong chip id high byte: %x", hi);
1963e8c08826SMartin Kepplinger 		return -ENXIO;
1964e8c08826SMartin Kepplinger 	}
1965e8c08826SMartin Kepplinger 
1966e8c08826SMartin Kepplinger 	dev_info(&client->dev, "chip id %02X %02X using %d mipi lanes\n",
1967e8c08826SMartin Kepplinger 		 hi, lo, hi846->nr_lanes);
1968e8c08826SMartin Kepplinger 
1969e8c08826SMartin Kepplinger 	return 0;
1970e8c08826SMartin Kepplinger }
1971e8c08826SMartin Kepplinger 
hi846_check_link_freqs(struct hi846 * hi846,struct v4l2_fwnode_endpoint * ep)1972e8c08826SMartin Kepplinger static s64 hi846_check_link_freqs(struct hi846 *hi846,
1973e8c08826SMartin Kepplinger 				  struct v4l2_fwnode_endpoint *ep)
1974e8c08826SMartin Kepplinger {
1975e8c08826SMartin Kepplinger 	const s64 *freqs = hi846_link_freqs;
1976e8c08826SMartin Kepplinger 	int freqs_count = ARRAY_SIZE(hi846_link_freqs);
1977e8c08826SMartin Kepplinger 	int i, j;
1978e8c08826SMartin Kepplinger 
1979e8c08826SMartin Kepplinger 	for (i = 0; i < freqs_count; i++) {
1980e8c08826SMartin Kepplinger 		for (j = 0; j < ep->nr_of_link_frequencies; j++)
1981e8c08826SMartin Kepplinger 			if (freqs[i] == ep->link_frequencies[j])
1982e8c08826SMartin Kepplinger 				break;
1983e8c08826SMartin Kepplinger 		if (j == ep->nr_of_link_frequencies)
1984e8c08826SMartin Kepplinger 			return freqs[i];
1985e8c08826SMartin Kepplinger 	}
1986e8c08826SMartin Kepplinger 
1987e8c08826SMartin Kepplinger 	return 0;
1988e8c08826SMartin Kepplinger }
1989e8c08826SMartin Kepplinger 
hi846_parse_dt(struct hi846 * hi846,struct device * dev)1990e8c08826SMartin Kepplinger static int hi846_parse_dt(struct hi846 *hi846, struct device *dev)
1991e8c08826SMartin Kepplinger {
1992e8c08826SMartin Kepplinger 	struct fwnode_handle *ep;
1993e8c08826SMartin Kepplinger 	struct fwnode_handle *fwnode = dev_fwnode(dev);
1994e8c08826SMartin Kepplinger 	struct v4l2_fwnode_endpoint bus_cfg = {
1995e8c08826SMartin Kepplinger 		.bus_type = V4L2_MBUS_CSI2_DPHY
1996e8c08826SMartin Kepplinger 	};
1997e8c08826SMartin Kepplinger 	int ret;
1998e8c08826SMartin Kepplinger 	s64 fq;
1999e8c08826SMartin Kepplinger 
2000e8c08826SMartin Kepplinger 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
2001e8c08826SMartin Kepplinger 	if (!ep) {
2002e8c08826SMartin Kepplinger 		dev_err(dev, "unable to find endpoint node\n");
2003e8c08826SMartin Kepplinger 		return -ENXIO;
2004e8c08826SMartin Kepplinger 	}
2005e8c08826SMartin Kepplinger 
2006e8c08826SMartin Kepplinger 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
2007e8c08826SMartin Kepplinger 	fwnode_handle_put(ep);
2008e8c08826SMartin Kepplinger 	if (ret) {
2009e8c08826SMartin Kepplinger 		dev_err(dev, "failed to parse endpoint node: %d\n", ret);
2010e8c08826SMartin Kepplinger 		return ret;
2011e8c08826SMartin Kepplinger 	}
2012e8c08826SMartin Kepplinger 
2013e8c08826SMartin Kepplinger 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
2014e8c08826SMartin Kepplinger 	    bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
2015e8c08826SMartin Kepplinger 		dev_err(dev, "number of CSI2 data lanes %d is not supported",
2016e8c08826SMartin Kepplinger 			bus_cfg.bus.mipi_csi2.num_data_lanes);
201780113026SRafael Mendonca 		ret = -EINVAL;
201880113026SRafael Mendonca 		goto check_hwcfg_error;
2019e8c08826SMartin Kepplinger 	}
2020e8c08826SMartin Kepplinger 
2021e8c08826SMartin Kepplinger 	hi846->nr_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
2022e8c08826SMartin Kepplinger 
2023e8c08826SMartin Kepplinger 	if (!bus_cfg.nr_of_link_frequencies) {
2024e8c08826SMartin Kepplinger 		dev_err(dev, "link-frequency property not found in DT\n");
202580113026SRafael Mendonca 		ret = -EINVAL;
202680113026SRafael Mendonca 		goto check_hwcfg_error;
2027e8c08826SMartin Kepplinger 	}
2028e8c08826SMartin Kepplinger 
2029e8c08826SMartin Kepplinger 	/* Check that link frequences for all the modes are in device tree */
2030e8c08826SMartin Kepplinger 	fq = hi846_check_link_freqs(hi846, &bus_cfg);
2031e8c08826SMartin Kepplinger 	if (fq) {
2032e8c08826SMartin Kepplinger 		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
203380113026SRafael Mendonca 		ret = -EINVAL;
203480113026SRafael Mendonca 		goto check_hwcfg_error;
2035e8c08826SMartin Kepplinger 	}
2036e8c08826SMartin Kepplinger 
2037e8c08826SMartin Kepplinger 	v4l2_fwnode_endpoint_free(&bus_cfg);
2038e8c08826SMartin Kepplinger 
2039e8c08826SMartin Kepplinger 	hi846->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
2040e8c08826SMartin Kepplinger 	if (IS_ERR(hi846->rst_gpio)) {
2041e8c08826SMartin Kepplinger 		dev_err(dev, "failed to get reset gpio: %pe\n",
2042e8c08826SMartin Kepplinger 			hi846->rst_gpio);
2043e8c08826SMartin Kepplinger 		return PTR_ERR(hi846->rst_gpio);
2044e8c08826SMartin Kepplinger 	}
2045e8c08826SMartin Kepplinger 
2046e8c08826SMartin Kepplinger 	hi846->shutdown_gpio = devm_gpiod_get_optional(dev, "shutdown",
2047e8c08826SMartin Kepplinger 						       GPIOD_OUT_LOW);
2048e8c08826SMartin Kepplinger 	if (IS_ERR(hi846->shutdown_gpio)) {
2049e8c08826SMartin Kepplinger 		dev_err(dev, "failed to get shutdown gpio: %pe\n",
2050e8c08826SMartin Kepplinger 			hi846->shutdown_gpio);
2051e8c08826SMartin Kepplinger 		return PTR_ERR(hi846->shutdown_gpio);
2052e8c08826SMartin Kepplinger 	}
2053e8c08826SMartin Kepplinger 
2054e8c08826SMartin Kepplinger 	return 0;
205580113026SRafael Mendonca 
205680113026SRafael Mendonca check_hwcfg_error:
205780113026SRafael Mendonca 	v4l2_fwnode_endpoint_free(&bus_cfg);
205880113026SRafael Mendonca 	return ret;
2059e8c08826SMartin Kepplinger }
2060e8c08826SMartin Kepplinger 
hi846_probe(struct i2c_client * client)2061e8c08826SMartin Kepplinger static int hi846_probe(struct i2c_client *client)
2062e8c08826SMartin Kepplinger {
2063e8c08826SMartin Kepplinger 	struct hi846 *hi846;
2064e8c08826SMartin Kepplinger 	int ret;
2065e8c08826SMartin Kepplinger 	int i;
2066e8c08826SMartin Kepplinger 	u32 mclk_freq;
2067e8c08826SMartin Kepplinger 
2068e8c08826SMartin Kepplinger 	hi846 = devm_kzalloc(&client->dev, sizeof(*hi846), GFP_KERNEL);
2069e8c08826SMartin Kepplinger 	if (!hi846)
2070e8c08826SMartin Kepplinger 		return -ENOMEM;
2071e8c08826SMartin Kepplinger 
2072e8c08826SMartin Kepplinger 	ret = hi846_parse_dt(hi846, &client->dev);
2073e8c08826SMartin Kepplinger 	if (ret) {
2074e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to check HW configuration: %d",
2075e8c08826SMartin Kepplinger 			ret);
2076e8c08826SMartin Kepplinger 		return ret;
2077e8c08826SMartin Kepplinger 	}
2078e8c08826SMartin Kepplinger 
2079e8c08826SMartin Kepplinger 	hi846->clock = devm_clk_get(&client->dev, NULL);
2080e8c08826SMartin Kepplinger 	if (IS_ERR(hi846->clock)) {
2081e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to get clock: %pe\n",
2082e8c08826SMartin Kepplinger 			hi846->clock);
2083e8c08826SMartin Kepplinger 		return PTR_ERR(hi846->clock);
2084e8c08826SMartin Kepplinger 	}
2085e8c08826SMartin Kepplinger 
2086e8c08826SMartin Kepplinger 	mclk_freq = clk_get_rate(hi846->clock);
2087e8c08826SMartin Kepplinger 	if (mclk_freq != 25000000)
2088e8c08826SMartin Kepplinger 		dev_warn(&client->dev,
2089e8c08826SMartin Kepplinger 			 "External clock freq should be 25000000, not %u.\n",
2090e8c08826SMartin Kepplinger 			 mclk_freq);
2091e8c08826SMartin Kepplinger 
2092e8c08826SMartin Kepplinger 	for (i = 0; i < HI846_NUM_SUPPLIES; i++)
2093e8c08826SMartin Kepplinger 		hi846->supplies[i].supply = hi846_supply_names[i];
2094e8c08826SMartin Kepplinger 
2095e8c08826SMartin Kepplinger 	ret = devm_regulator_bulk_get(&client->dev, HI846_NUM_SUPPLIES,
2096e8c08826SMartin Kepplinger 				      hi846->supplies);
2097e8c08826SMartin Kepplinger 	if (ret < 0)
2098e8c08826SMartin Kepplinger 		return ret;
2099e8c08826SMartin Kepplinger 
2100e8c08826SMartin Kepplinger 	v4l2_i2c_subdev_init(&hi846->sd, client, &hi846_subdev_ops);
2101e8c08826SMartin Kepplinger 
2102e8c08826SMartin Kepplinger 	mutex_init(&hi846->mutex);
2103e8c08826SMartin Kepplinger 
2104e8c08826SMartin Kepplinger 	ret = hi846_power_on(hi846);
2105e8c08826SMartin Kepplinger 	if (ret)
2106e8c08826SMartin Kepplinger 		goto err_mutex;
2107e8c08826SMartin Kepplinger 
2108e8c08826SMartin Kepplinger 	ret = hi846_identify_module(hi846);
2109e8c08826SMartin Kepplinger 	if (ret)
2110e8c08826SMartin Kepplinger 		goto err_power_off;
2111e8c08826SMartin Kepplinger 
2112e8c08826SMartin Kepplinger 	hi846->cur_mode = &supported_modes[0];
2113e8c08826SMartin Kepplinger 
2114e8c08826SMartin Kepplinger 	ret = hi846_init_controls(hi846);
2115e8c08826SMartin Kepplinger 	if (ret) {
2116e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to init controls: %d", ret);
2117e8c08826SMartin Kepplinger 		goto err_power_off;
2118e8c08826SMartin Kepplinger 	}
2119e8c08826SMartin Kepplinger 
2120e8c08826SMartin Kepplinger 	hi846->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
2121e8c08826SMartin Kepplinger 	hi846->sd.entity.ops = &hi846_subdev_entity_ops;
2122e8c08826SMartin Kepplinger 	hi846->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
2123e8c08826SMartin Kepplinger 	hi846->pad.flags = MEDIA_PAD_FL_SOURCE;
2124e8c08826SMartin Kepplinger 	ret = media_entity_pads_init(&hi846->sd.entity, 1, &hi846->pad);
2125e8c08826SMartin Kepplinger 	if (ret) {
2126e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to init entity pads: %d", ret);
2127e8c08826SMartin Kepplinger 		goto err_v4l2_ctrl_handler_free;
2128e8c08826SMartin Kepplinger 	}
2129e8c08826SMartin Kepplinger 
2130e8c08826SMartin Kepplinger 	ret = v4l2_async_register_subdev_sensor(&hi846->sd);
2131e8c08826SMartin Kepplinger 	if (ret < 0) {
2132e8c08826SMartin Kepplinger 		dev_err(&client->dev, "failed to register V4L2 subdev: %d",
2133e8c08826SMartin Kepplinger 			ret);
2134e8c08826SMartin Kepplinger 		goto err_media_entity_cleanup;
2135e8c08826SMartin Kepplinger 	}
2136e8c08826SMartin Kepplinger 
2137e8c08826SMartin Kepplinger 	pm_runtime_set_active(&client->dev);
2138e8c08826SMartin Kepplinger 	pm_runtime_enable(&client->dev);
2139e8c08826SMartin Kepplinger 	pm_runtime_idle(&client->dev);
2140e8c08826SMartin Kepplinger 
2141e8c08826SMartin Kepplinger 	return 0;
2142e8c08826SMartin Kepplinger 
2143e8c08826SMartin Kepplinger err_media_entity_cleanup:
2144e8c08826SMartin Kepplinger 	media_entity_cleanup(&hi846->sd.entity);
2145e8c08826SMartin Kepplinger 
2146e8c08826SMartin Kepplinger err_v4l2_ctrl_handler_free:
2147e8c08826SMartin Kepplinger 	v4l2_ctrl_handler_free(hi846->sd.ctrl_handler);
2148e8c08826SMartin Kepplinger 
2149e8c08826SMartin Kepplinger err_power_off:
2150e8c08826SMartin Kepplinger 	hi846_power_off(hi846);
2151e8c08826SMartin Kepplinger 
2152e8c08826SMartin Kepplinger err_mutex:
2153e8c08826SMartin Kepplinger 	mutex_destroy(&hi846->mutex);
2154e8c08826SMartin Kepplinger 
2155e8c08826SMartin Kepplinger 	return ret;
2156e8c08826SMartin Kepplinger }
2157e8c08826SMartin Kepplinger 
hi846_remove(struct i2c_client * client)2158ed5c2f5fSUwe Kleine-König static void hi846_remove(struct i2c_client *client)
2159e8c08826SMartin Kepplinger {
2160e8c08826SMartin Kepplinger 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
2161e8c08826SMartin Kepplinger 	struct hi846 *hi846 = to_hi846(sd);
2162e8c08826SMartin Kepplinger 
2163e8c08826SMartin Kepplinger 	v4l2_async_unregister_subdev(sd);
2164e8c08826SMartin Kepplinger 	media_entity_cleanup(&sd->entity);
2165e8c08826SMartin Kepplinger 	v4l2_ctrl_handler_free(sd->ctrl_handler);
2166e8c08826SMartin Kepplinger 
2167e8c08826SMartin Kepplinger 	pm_runtime_disable(&client->dev);
2168e8c08826SMartin Kepplinger 	if (!pm_runtime_status_suspended(&client->dev))
2169e8c08826SMartin Kepplinger 		hi846_suspend(&client->dev);
2170e8c08826SMartin Kepplinger 	pm_runtime_set_suspended(&client->dev);
2171e8c08826SMartin Kepplinger 
2172e8c08826SMartin Kepplinger 	mutex_destroy(&hi846->mutex);
2173e8c08826SMartin Kepplinger }
2174e8c08826SMartin Kepplinger 
217569a18745SMartin Kepplinger static const struct dev_pm_ops hi846_pm_ops = {
217669a18745SMartin Kepplinger 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
217769a18745SMartin Kepplinger 				pm_runtime_force_resume)
217869a18745SMartin Kepplinger 	SET_RUNTIME_PM_OPS(hi846_suspend, hi846_resume, NULL)
217969a18745SMartin Kepplinger };
2180e8c08826SMartin Kepplinger 
2181e8c08826SMartin Kepplinger static const struct of_device_id hi846_of_match[] = {
2182e8c08826SMartin Kepplinger 	{ .compatible = "hynix,hi846", },
2183e8c08826SMartin Kepplinger 	{},
2184e8c08826SMartin Kepplinger };
2185e8c08826SMartin Kepplinger MODULE_DEVICE_TABLE(of, hi846_of_match);
2186e8c08826SMartin Kepplinger 
2187e8c08826SMartin Kepplinger static struct i2c_driver hi846_i2c_driver = {
2188e8c08826SMartin Kepplinger 	.driver = {
2189e8c08826SMartin Kepplinger 		.name = "hi846",
2190e8c08826SMartin Kepplinger 		.pm = &hi846_pm_ops,
219162456590SMartin Kepplinger 		.of_match_table = hi846_of_match,
2192e8c08826SMartin Kepplinger 	},
2193aaeb31c0SUwe Kleine-König 	.probe = hi846_probe,
2194e8c08826SMartin Kepplinger 	.remove = hi846_remove,
2195e8c08826SMartin Kepplinger };
2196e8c08826SMartin Kepplinger 
2197e8c08826SMartin Kepplinger module_i2c_driver(hi846_i2c_driver);
2198e8c08826SMartin Kepplinger 
2199e8c08826SMartin Kepplinger MODULE_AUTHOR("Angus Ainslie <angus@akkea.ca>");
2200e8c08826SMartin Kepplinger MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
2201e8c08826SMartin Kepplinger MODULE_DESCRIPTION("Hynix HI846 sensor driver");
2202e8c08826SMartin Kepplinger MODULE_LICENSE("GPL v2");
2203