xref: /openbmc/linux/drivers/media/i2c/imx258.c (revision d30ac9d8)
1e4802cb0SJason Chen // SPDX-License-Identifier: GPL-2.0
2e4802cb0SJason Chen // Copyright (C) 2018 Intel Corporation
3e4802cb0SJason Chen 
4e4802cb0SJason Chen #include <linux/acpi.h>
5e4802cb0SJason Chen #include <linux/delay.h>
6e4802cb0SJason Chen #include <linux/i2c.h>
7e4802cb0SJason Chen #include <linux/module.h>
8e4802cb0SJason Chen #include <linux/pm_runtime.h>
9e4802cb0SJason Chen #include <media/v4l2-ctrls.h>
10e4802cb0SJason Chen #include <media/v4l2-device.h>
11e4802cb0SJason Chen #include <asm/unaligned.h>
12e4802cb0SJason Chen 
13e4802cb0SJason Chen #define IMX258_REG_VALUE_08BIT		1
14e4802cb0SJason Chen #define IMX258_REG_VALUE_16BIT		2
15e4802cb0SJason Chen 
16e4802cb0SJason Chen #define IMX258_REG_MODE_SELECT		0x0100
17e4802cb0SJason Chen #define IMX258_MODE_STANDBY		0x00
18e4802cb0SJason Chen #define IMX258_MODE_STREAMING		0x01
19e4802cb0SJason Chen 
20e4802cb0SJason Chen /* Chip ID */
21e4802cb0SJason Chen #define IMX258_REG_CHIP_ID		0x0016
22e4802cb0SJason Chen #define IMX258_CHIP_ID			0x0258
23e4802cb0SJason Chen 
24e4802cb0SJason Chen /* V_TIMING internal */
25e4802cb0SJason Chen #define IMX258_VTS_30FPS		0x0c98
26e4802cb0SJason Chen #define IMX258_VTS_30FPS_2K		0x0638
27e4802cb0SJason Chen #define IMX258_VTS_30FPS_VGA		0x034c
28e4802cb0SJason Chen #define IMX258_VTS_MAX			0xffff
29e4802cb0SJason Chen 
30e4802cb0SJason Chen /*Frame Length Line*/
31e4802cb0SJason Chen #define IMX258_FLL_MIN			0x08a6
32e4802cb0SJason Chen #define IMX258_FLL_MAX			0xffff
33e4802cb0SJason Chen #define IMX258_FLL_STEP			1
34e4802cb0SJason Chen #define IMX258_FLL_DEFAULT		0x0c98
35e4802cb0SJason Chen 
36e4802cb0SJason Chen /* HBLANK control - read only */
37e4802cb0SJason Chen #define IMX258_PPL_DEFAULT		5352
38e4802cb0SJason Chen 
39e4802cb0SJason Chen /* Exposure control */
40e4802cb0SJason Chen #define IMX258_REG_EXPOSURE		0x0202
41e4802cb0SJason Chen #define IMX258_EXPOSURE_MIN		4
42e4802cb0SJason Chen #define IMX258_EXPOSURE_STEP		1
43e4802cb0SJason Chen #define IMX258_EXPOSURE_DEFAULT		0x640
44e4802cb0SJason Chen #define IMX258_EXPOSURE_MAX		65535
45e4802cb0SJason Chen 
46e4802cb0SJason Chen /* Analog gain control */
47e4802cb0SJason Chen #define IMX258_REG_ANALOG_GAIN		0x0204
48e4802cb0SJason Chen #define IMX258_ANA_GAIN_MIN		0
49e4802cb0SJason Chen #define IMX258_ANA_GAIN_MAX		0x1fff
50e4802cb0SJason Chen #define IMX258_ANA_GAIN_STEP		1
51e4802cb0SJason Chen #define IMX258_ANA_GAIN_DEFAULT		0x0
52e4802cb0SJason Chen 
53e4802cb0SJason Chen /* Digital gain control */
54e4802cb0SJason Chen #define IMX258_REG_GR_DIGITAL_GAIN	0x020e
55e4802cb0SJason Chen #define IMX258_REG_R_DIGITAL_GAIN	0x0210
56e4802cb0SJason Chen #define IMX258_REG_B_DIGITAL_GAIN	0x0212
57e4802cb0SJason Chen #define IMX258_REG_GB_DIGITAL_GAIN	0x0214
58e4802cb0SJason Chen #define IMX258_DGTL_GAIN_MIN		0
59e4802cb0SJason Chen #define IMX258_DGTL_GAIN_MAX		4096	/* Max = 0xFFF */
60e4802cb0SJason Chen #define IMX258_DGTL_GAIN_DEFAULT	1024
61e4802cb0SJason Chen #define IMX258_DGTL_GAIN_STEP		1
62e4802cb0SJason Chen 
63e4802cb0SJason Chen /* Test Pattern Control */
64e4802cb0SJason Chen #define IMX258_REG_TEST_PATTERN		0x0600
65e4802cb0SJason Chen 
66e4802cb0SJason Chen /* Orientation */
67e4802cb0SJason Chen #define REG_MIRROR_FLIP_CONTROL		0x0101
68e4802cb0SJason Chen #define REG_CONFIG_MIRROR_FLIP		0x03
69e4802cb0SJason Chen #define REG_CONFIG_FLIP_TEST_PATTERN	0x02
70e4802cb0SJason Chen 
71e4802cb0SJason Chen struct imx258_reg {
72e4802cb0SJason Chen 	u16 address;
73e4802cb0SJason Chen 	u8 val;
74e4802cb0SJason Chen };
75e4802cb0SJason Chen 
76e4802cb0SJason Chen struct imx258_reg_list {
77e4802cb0SJason Chen 	u32 num_of_regs;
78e4802cb0SJason Chen 	const struct imx258_reg *regs;
79e4802cb0SJason Chen };
80e4802cb0SJason Chen 
81e4802cb0SJason Chen /* Link frequency config */
82e4802cb0SJason Chen struct imx258_link_freq_config {
83e4802cb0SJason Chen 	u32 pixels_per_line;
84e4802cb0SJason Chen 
85e4802cb0SJason Chen 	/* PLL registers for this link frequency */
86e4802cb0SJason Chen 	struct imx258_reg_list reg_list;
87e4802cb0SJason Chen };
88e4802cb0SJason Chen 
89e4802cb0SJason Chen /* Mode : resolution and related config&values */
90e4802cb0SJason Chen struct imx258_mode {
91e4802cb0SJason Chen 	/* Frame width */
92e4802cb0SJason Chen 	u32 width;
93e4802cb0SJason Chen 	/* Frame height */
94e4802cb0SJason Chen 	u32 height;
95e4802cb0SJason Chen 
96e4802cb0SJason Chen 	/* V-timing */
97e4802cb0SJason Chen 	u32 vts_def;
98e4802cb0SJason Chen 	u32 vts_min;
99e4802cb0SJason Chen 
100e4802cb0SJason Chen 	/* Index of Link frequency config to be used */
101e4802cb0SJason Chen 	u32 link_freq_index;
102e4802cb0SJason Chen 	/* Default register values */
103e4802cb0SJason Chen 	struct imx258_reg_list reg_list;
104e4802cb0SJason Chen };
105e4802cb0SJason Chen 
106e4802cb0SJason Chen /* 4208x3118 needs 1267Mbps/lane, 4 lanes */
107e4802cb0SJason Chen static const struct imx258_reg mipi_data_rate_1267mbps[] = {
108e4802cb0SJason Chen 	{ 0x0301, 0x05 },
109e4802cb0SJason Chen 	{ 0x0303, 0x02 },
110e4802cb0SJason Chen 	{ 0x0305, 0x03 },
111e4802cb0SJason Chen 	{ 0x0306, 0x00 },
112e4802cb0SJason Chen 	{ 0x0307, 0xC6 },
113e4802cb0SJason Chen 	{ 0x0309, 0x0A },
114e4802cb0SJason Chen 	{ 0x030B, 0x01 },
115e4802cb0SJason Chen 	{ 0x030D, 0x02 },
116e4802cb0SJason Chen 	{ 0x030E, 0x00 },
117e4802cb0SJason Chen 	{ 0x030F, 0xD8 },
118e4802cb0SJason Chen 	{ 0x0310, 0x00 },
119e4802cb0SJason Chen 	{ 0x0820, 0x13 },
120e4802cb0SJason Chen 	{ 0x0821, 0x4C },
121e4802cb0SJason Chen 	{ 0x0822, 0xCC },
122e4802cb0SJason Chen 	{ 0x0823, 0xCC },
123e4802cb0SJason Chen };
124e4802cb0SJason Chen 
125e4802cb0SJason Chen static const struct imx258_reg mipi_data_rate_640mbps[] = {
126e4802cb0SJason Chen 	{ 0x0301, 0x05 },
127e4802cb0SJason Chen 	{ 0x0303, 0x02 },
128e4802cb0SJason Chen 	{ 0x0305, 0x03 },
129e4802cb0SJason Chen 	{ 0x0306, 0x00 },
130e4802cb0SJason Chen 	{ 0x0307, 0x64 },
131e4802cb0SJason Chen 	{ 0x0309, 0x0A },
132e4802cb0SJason Chen 	{ 0x030B, 0x01 },
133e4802cb0SJason Chen 	{ 0x030D, 0x02 },
134e4802cb0SJason Chen 	{ 0x030E, 0x00 },
135e4802cb0SJason Chen 	{ 0x030F, 0xD8 },
136e4802cb0SJason Chen 	{ 0x0310, 0x00 },
137e4802cb0SJason Chen 	{ 0x0820, 0x0A },
138e4802cb0SJason Chen 	{ 0x0821, 0x00 },
139e4802cb0SJason Chen 	{ 0x0822, 0x00 },
140e4802cb0SJason Chen 	{ 0x0823, 0x00 },
141e4802cb0SJason Chen };
142e4802cb0SJason Chen 
143e4802cb0SJason Chen static const struct imx258_reg mode_4208x3118_regs[] = {
144e4802cb0SJason Chen 	{ 0x0136, 0x13 },
145e4802cb0SJason Chen 	{ 0x0137, 0x33 },
146e4802cb0SJason Chen 	{ 0x3051, 0x00 },
147e4802cb0SJason Chen 	{ 0x3052, 0x00 },
148e4802cb0SJason Chen 	{ 0x4E21, 0x14 },
149e4802cb0SJason Chen 	{ 0x6B11, 0xCF },
150e4802cb0SJason Chen 	{ 0x7FF0, 0x08 },
151e4802cb0SJason Chen 	{ 0x7FF1, 0x0F },
152e4802cb0SJason Chen 	{ 0x7FF2, 0x08 },
153e4802cb0SJason Chen 	{ 0x7FF3, 0x1B },
154e4802cb0SJason Chen 	{ 0x7FF4, 0x23 },
155e4802cb0SJason Chen 	{ 0x7FF5, 0x60 },
156e4802cb0SJason Chen 	{ 0x7FF6, 0x00 },
157e4802cb0SJason Chen 	{ 0x7FF7, 0x01 },
158e4802cb0SJason Chen 	{ 0x7FF8, 0x00 },
159e4802cb0SJason Chen 	{ 0x7FF9, 0x78 },
160e4802cb0SJason Chen 	{ 0x7FFA, 0x00 },
161e4802cb0SJason Chen 	{ 0x7FFB, 0x00 },
162e4802cb0SJason Chen 	{ 0x7FFC, 0x00 },
163e4802cb0SJason Chen 	{ 0x7FFD, 0x00 },
164e4802cb0SJason Chen 	{ 0x7FFE, 0x00 },
165e4802cb0SJason Chen 	{ 0x7FFF, 0x03 },
166e4802cb0SJason Chen 	{ 0x7F76, 0x03 },
167e4802cb0SJason Chen 	{ 0x7F77, 0xFE },
168e4802cb0SJason Chen 	{ 0x7FA8, 0x03 },
169e4802cb0SJason Chen 	{ 0x7FA9, 0xFE },
170e4802cb0SJason Chen 	{ 0x7B24, 0x81 },
171e4802cb0SJason Chen 	{ 0x7B25, 0x00 },
172e4802cb0SJason Chen 	{ 0x6564, 0x07 },
173e4802cb0SJason Chen 	{ 0x6B0D, 0x41 },
174e4802cb0SJason Chen 	{ 0x653D, 0x04 },
175e4802cb0SJason Chen 	{ 0x6B05, 0x8C },
176e4802cb0SJason Chen 	{ 0x6B06, 0xF9 },
177e4802cb0SJason Chen 	{ 0x6B08, 0x65 },
178e4802cb0SJason Chen 	{ 0x6B09, 0xFC },
179e4802cb0SJason Chen 	{ 0x6B0A, 0xCF },
180e4802cb0SJason Chen 	{ 0x6B0B, 0xD2 },
181e4802cb0SJason Chen 	{ 0x6700, 0x0E },
182e4802cb0SJason Chen 	{ 0x6707, 0x0E },
183e4802cb0SJason Chen 	{ 0x9104, 0x00 },
184e4802cb0SJason Chen 	{ 0x4648, 0x7F },
185e4802cb0SJason Chen 	{ 0x7420, 0x00 },
186e4802cb0SJason Chen 	{ 0x7421, 0x1C },
187e4802cb0SJason Chen 	{ 0x7422, 0x00 },
188e4802cb0SJason Chen 	{ 0x7423, 0xD7 },
189e4802cb0SJason Chen 	{ 0x5F04, 0x00 },
190e4802cb0SJason Chen 	{ 0x5F05, 0xED },
191e4802cb0SJason Chen 	{ 0x0112, 0x0A },
192e4802cb0SJason Chen 	{ 0x0113, 0x0A },
193e4802cb0SJason Chen 	{ 0x0114, 0x03 },
194e4802cb0SJason Chen 	{ 0x0342, 0x14 },
195e4802cb0SJason Chen 	{ 0x0343, 0xE8 },
196e4802cb0SJason Chen 	{ 0x0340, 0x0C },
197e4802cb0SJason Chen 	{ 0x0341, 0x50 },
198e4802cb0SJason Chen 	{ 0x0344, 0x00 },
199e4802cb0SJason Chen 	{ 0x0345, 0x00 },
200e4802cb0SJason Chen 	{ 0x0346, 0x00 },
201e4802cb0SJason Chen 	{ 0x0347, 0x00 },
202e4802cb0SJason Chen 	{ 0x0348, 0x10 },
203e4802cb0SJason Chen 	{ 0x0349, 0x6F },
204e4802cb0SJason Chen 	{ 0x034A, 0x0C },
205e4802cb0SJason Chen 	{ 0x034B, 0x2E },
206e4802cb0SJason Chen 	{ 0x0381, 0x01 },
207e4802cb0SJason Chen 	{ 0x0383, 0x01 },
208e4802cb0SJason Chen 	{ 0x0385, 0x01 },
209e4802cb0SJason Chen 	{ 0x0387, 0x01 },
210e4802cb0SJason Chen 	{ 0x0900, 0x00 },
211e4802cb0SJason Chen 	{ 0x0901, 0x11 },
212e4802cb0SJason Chen 	{ 0x0401, 0x00 },
213e4802cb0SJason Chen 	{ 0x0404, 0x00 },
214e4802cb0SJason Chen 	{ 0x0405, 0x10 },
215e4802cb0SJason Chen 	{ 0x0408, 0x00 },
216e4802cb0SJason Chen 	{ 0x0409, 0x00 },
217e4802cb0SJason Chen 	{ 0x040A, 0x00 },
218e4802cb0SJason Chen 	{ 0x040B, 0x00 },
219e4802cb0SJason Chen 	{ 0x040C, 0x10 },
220e4802cb0SJason Chen 	{ 0x040D, 0x70 },
221e4802cb0SJason Chen 	{ 0x040E, 0x0C },
222e4802cb0SJason Chen 	{ 0x040F, 0x30 },
223e4802cb0SJason Chen 	{ 0x3038, 0x00 },
224e4802cb0SJason Chen 	{ 0x303A, 0x00 },
225e4802cb0SJason Chen 	{ 0x303B, 0x10 },
226e4802cb0SJason Chen 	{ 0x300D, 0x00 },
227e4802cb0SJason Chen 	{ 0x034C, 0x10 },
228e4802cb0SJason Chen 	{ 0x034D, 0x70 },
229e4802cb0SJason Chen 	{ 0x034E, 0x0C },
230e4802cb0SJason Chen 	{ 0x034F, 0x30 },
231e4802cb0SJason Chen 	{ 0x0350, 0x01 },
232e4802cb0SJason Chen 	{ 0x0202, 0x0C },
233e4802cb0SJason Chen 	{ 0x0203, 0x46 },
234e4802cb0SJason Chen 	{ 0x0204, 0x00 },
235e4802cb0SJason Chen 	{ 0x0205, 0x00 },
236e4802cb0SJason Chen 	{ 0x020E, 0x01 },
237e4802cb0SJason Chen 	{ 0x020F, 0x00 },
238e4802cb0SJason Chen 	{ 0x0210, 0x01 },
239e4802cb0SJason Chen 	{ 0x0211, 0x00 },
240e4802cb0SJason Chen 	{ 0x0212, 0x01 },
241e4802cb0SJason Chen 	{ 0x0213, 0x00 },
242e4802cb0SJason Chen 	{ 0x0214, 0x01 },
243e4802cb0SJason Chen 	{ 0x0215, 0x00 },
244e4802cb0SJason Chen 	{ 0x7BCD, 0x00 },
245e4802cb0SJason Chen 	{ 0x94DC, 0x20 },
246e4802cb0SJason Chen 	{ 0x94DD, 0x20 },
247e4802cb0SJason Chen 	{ 0x94DE, 0x20 },
248e4802cb0SJason Chen 	{ 0x95DC, 0x20 },
249e4802cb0SJason Chen 	{ 0x95DD, 0x20 },
250e4802cb0SJason Chen 	{ 0x95DE, 0x20 },
251e4802cb0SJason Chen 	{ 0x7FB0, 0x00 },
252e4802cb0SJason Chen 	{ 0x9010, 0x3E },
253e4802cb0SJason Chen 	{ 0x9419, 0x50 },
254e4802cb0SJason Chen 	{ 0x941B, 0x50 },
255e4802cb0SJason Chen 	{ 0x9519, 0x50 },
256e4802cb0SJason Chen 	{ 0x951B, 0x50 },
257e4802cb0SJason Chen 	{ 0x3030, 0x00 },
258e4802cb0SJason Chen 	{ 0x3032, 0x00 },
259e4802cb0SJason Chen 	{ 0x0220, 0x00 },
260e4802cb0SJason Chen };
261e4802cb0SJason Chen 
262e4802cb0SJason Chen static const struct imx258_reg mode_2104_1560_regs[] = {
263e4802cb0SJason Chen 	{ 0x0136, 0x13 },
264e4802cb0SJason Chen 	{ 0x0137, 0x33 },
265e4802cb0SJason Chen 	{ 0x3051, 0x00 },
266e4802cb0SJason Chen 	{ 0x3052, 0x00 },
267e4802cb0SJason Chen 	{ 0x4E21, 0x14 },
268e4802cb0SJason Chen 	{ 0x6B11, 0xCF },
269e4802cb0SJason Chen 	{ 0x7FF0, 0x08 },
270e4802cb0SJason Chen 	{ 0x7FF1, 0x0F },
271e4802cb0SJason Chen 	{ 0x7FF2, 0x08 },
272e4802cb0SJason Chen 	{ 0x7FF3, 0x1B },
273e4802cb0SJason Chen 	{ 0x7FF4, 0x23 },
274e4802cb0SJason Chen 	{ 0x7FF5, 0x60 },
275e4802cb0SJason Chen 	{ 0x7FF6, 0x00 },
276e4802cb0SJason Chen 	{ 0x7FF7, 0x01 },
277e4802cb0SJason Chen 	{ 0x7FF8, 0x00 },
278e4802cb0SJason Chen 	{ 0x7FF9, 0x78 },
279e4802cb0SJason Chen 	{ 0x7FFA, 0x00 },
280e4802cb0SJason Chen 	{ 0x7FFB, 0x00 },
281e4802cb0SJason Chen 	{ 0x7FFC, 0x00 },
282e4802cb0SJason Chen 	{ 0x7FFD, 0x00 },
283e4802cb0SJason Chen 	{ 0x7FFE, 0x00 },
284e4802cb0SJason Chen 	{ 0x7FFF, 0x03 },
285e4802cb0SJason Chen 	{ 0x7F76, 0x03 },
286e4802cb0SJason Chen 	{ 0x7F77, 0xFE },
287e4802cb0SJason Chen 	{ 0x7FA8, 0x03 },
288e4802cb0SJason Chen 	{ 0x7FA9, 0xFE },
289e4802cb0SJason Chen 	{ 0x7B24, 0x81 },
290e4802cb0SJason Chen 	{ 0x7B25, 0x00 },
291e4802cb0SJason Chen 	{ 0x6564, 0x07 },
292e4802cb0SJason Chen 	{ 0x6B0D, 0x41 },
293e4802cb0SJason Chen 	{ 0x653D, 0x04 },
294e4802cb0SJason Chen 	{ 0x6B05, 0x8C },
295e4802cb0SJason Chen 	{ 0x6B06, 0xF9 },
296e4802cb0SJason Chen 	{ 0x6B08, 0x65 },
297e4802cb0SJason Chen 	{ 0x6B09, 0xFC },
298e4802cb0SJason Chen 	{ 0x6B0A, 0xCF },
299e4802cb0SJason Chen 	{ 0x6B0B, 0xD2 },
300e4802cb0SJason Chen 	{ 0x6700, 0x0E },
301e4802cb0SJason Chen 	{ 0x6707, 0x0E },
302e4802cb0SJason Chen 	{ 0x9104, 0x00 },
303e4802cb0SJason Chen 	{ 0x4648, 0x7F },
304e4802cb0SJason Chen 	{ 0x7420, 0x00 },
305e4802cb0SJason Chen 	{ 0x7421, 0x1C },
306e4802cb0SJason Chen 	{ 0x7422, 0x00 },
307e4802cb0SJason Chen 	{ 0x7423, 0xD7 },
308e4802cb0SJason Chen 	{ 0x5F04, 0x00 },
309e4802cb0SJason Chen 	{ 0x5F05, 0xED },
310e4802cb0SJason Chen 	{ 0x0112, 0x0A },
311e4802cb0SJason Chen 	{ 0x0113, 0x0A },
312e4802cb0SJason Chen 	{ 0x0114, 0x03 },
313e4802cb0SJason Chen 	{ 0x0342, 0x14 },
314e4802cb0SJason Chen 	{ 0x0343, 0xE8 },
315e4802cb0SJason Chen 	{ 0x0340, 0x06 },
316e4802cb0SJason Chen 	{ 0x0341, 0x38 },
317e4802cb0SJason Chen 	{ 0x0344, 0x00 },
318e4802cb0SJason Chen 	{ 0x0345, 0x00 },
319e4802cb0SJason Chen 	{ 0x0346, 0x00 },
320e4802cb0SJason Chen 	{ 0x0347, 0x00 },
321e4802cb0SJason Chen 	{ 0x0348, 0x10 },
322e4802cb0SJason Chen 	{ 0x0349, 0x6F },
323e4802cb0SJason Chen 	{ 0x034A, 0x0C },
324e4802cb0SJason Chen 	{ 0x034B, 0x2E },
325e4802cb0SJason Chen 	{ 0x0381, 0x01 },
326e4802cb0SJason Chen 	{ 0x0383, 0x01 },
327e4802cb0SJason Chen 	{ 0x0385, 0x01 },
328e4802cb0SJason Chen 	{ 0x0387, 0x01 },
329e4802cb0SJason Chen 	{ 0x0900, 0x01 },
330e4802cb0SJason Chen 	{ 0x0901, 0x12 },
331e4802cb0SJason Chen 	{ 0x0401, 0x01 },
332e4802cb0SJason Chen 	{ 0x0404, 0x00 },
333e4802cb0SJason Chen 	{ 0x0405, 0x20 },
334e4802cb0SJason Chen 	{ 0x0408, 0x00 },
335e4802cb0SJason Chen 	{ 0x0409, 0x02 },
336e4802cb0SJason Chen 	{ 0x040A, 0x00 },
337e4802cb0SJason Chen 	{ 0x040B, 0x00 },
338e4802cb0SJason Chen 	{ 0x040C, 0x10 },
339e4802cb0SJason Chen 	{ 0x040D, 0x6A },
340e4802cb0SJason Chen 	{ 0x040E, 0x06 },
341e4802cb0SJason Chen 	{ 0x040F, 0x18 },
342e4802cb0SJason Chen 	{ 0x3038, 0x00 },
343e4802cb0SJason Chen 	{ 0x303A, 0x00 },
344e4802cb0SJason Chen 	{ 0x303B, 0x10 },
345e4802cb0SJason Chen 	{ 0x300D, 0x00 },
346e4802cb0SJason Chen 	{ 0x034C, 0x08 },
347e4802cb0SJason Chen 	{ 0x034D, 0x38 },
348e4802cb0SJason Chen 	{ 0x034E, 0x06 },
349e4802cb0SJason Chen 	{ 0x034F, 0x18 },
350e4802cb0SJason Chen 	{ 0x0350, 0x01 },
351e4802cb0SJason Chen 	{ 0x0202, 0x06 },
352e4802cb0SJason Chen 	{ 0x0203, 0x2E },
353e4802cb0SJason Chen 	{ 0x0204, 0x00 },
354e4802cb0SJason Chen 	{ 0x0205, 0x00 },
355e4802cb0SJason Chen 	{ 0x020E, 0x01 },
356e4802cb0SJason Chen 	{ 0x020F, 0x00 },
357e4802cb0SJason Chen 	{ 0x0210, 0x01 },
358e4802cb0SJason Chen 	{ 0x0211, 0x00 },
359e4802cb0SJason Chen 	{ 0x0212, 0x01 },
360e4802cb0SJason Chen 	{ 0x0213, 0x00 },
361e4802cb0SJason Chen 	{ 0x0214, 0x01 },
362e4802cb0SJason Chen 	{ 0x0215, 0x00 },
363e4802cb0SJason Chen 	{ 0x7BCD, 0x01 },
364e4802cb0SJason Chen 	{ 0x94DC, 0x20 },
365e4802cb0SJason Chen 	{ 0x94DD, 0x20 },
366e4802cb0SJason Chen 	{ 0x94DE, 0x20 },
367e4802cb0SJason Chen 	{ 0x95DC, 0x20 },
368e4802cb0SJason Chen 	{ 0x95DD, 0x20 },
369e4802cb0SJason Chen 	{ 0x95DE, 0x20 },
370e4802cb0SJason Chen 	{ 0x7FB0, 0x00 },
371e4802cb0SJason Chen 	{ 0x9010, 0x3E },
372e4802cb0SJason Chen 	{ 0x9419, 0x50 },
373e4802cb0SJason Chen 	{ 0x941B, 0x50 },
374e4802cb0SJason Chen 	{ 0x9519, 0x50 },
375e4802cb0SJason Chen 	{ 0x951B, 0x50 },
376e4802cb0SJason Chen 	{ 0x3030, 0x00 },
377e4802cb0SJason Chen 	{ 0x3032, 0x00 },
378e4802cb0SJason Chen 	{ 0x0220, 0x00 },
379e4802cb0SJason Chen };
380e4802cb0SJason Chen 
381e4802cb0SJason Chen static const struct imx258_reg mode_1048_780_regs[] = {
382e4802cb0SJason Chen 	{ 0x0136, 0x13 },
383e4802cb0SJason Chen 	{ 0x0137, 0x33 },
384e4802cb0SJason Chen 	{ 0x3051, 0x00 },
385e4802cb0SJason Chen 	{ 0x3052, 0x00 },
386e4802cb0SJason Chen 	{ 0x4E21, 0x14 },
387e4802cb0SJason Chen 	{ 0x6B11, 0xCF },
388e4802cb0SJason Chen 	{ 0x7FF0, 0x08 },
389e4802cb0SJason Chen 	{ 0x7FF1, 0x0F },
390e4802cb0SJason Chen 	{ 0x7FF2, 0x08 },
391e4802cb0SJason Chen 	{ 0x7FF3, 0x1B },
392e4802cb0SJason Chen 	{ 0x7FF4, 0x23 },
393e4802cb0SJason Chen 	{ 0x7FF5, 0x60 },
394e4802cb0SJason Chen 	{ 0x7FF6, 0x00 },
395e4802cb0SJason Chen 	{ 0x7FF7, 0x01 },
396e4802cb0SJason Chen 	{ 0x7FF8, 0x00 },
397e4802cb0SJason Chen 	{ 0x7FF9, 0x78 },
398e4802cb0SJason Chen 	{ 0x7FFA, 0x00 },
399e4802cb0SJason Chen 	{ 0x7FFB, 0x00 },
400e4802cb0SJason Chen 	{ 0x7FFC, 0x00 },
401e4802cb0SJason Chen 	{ 0x7FFD, 0x00 },
402e4802cb0SJason Chen 	{ 0x7FFE, 0x00 },
403e4802cb0SJason Chen 	{ 0x7FFF, 0x03 },
404e4802cb0SJason Chen 	{ 0x7F76, 0x03 },
405e4802cb0SJason Chen 	{ 0x7F77, 0xFE },
406e4802cb0SJason Chen 	{ 0x7FA8, 0x03 },
407e4802cb0SJason Chen 	{ 0x7FA9, 0xFE },
408e4802cb0SJason Chen 	{ 0x7B24, 0x81 },
409e4802cb0SJason Chen 	{ 0x7B25, 0x00 },
410e4802cb0SJason Chen 	{ 0x6564, 0x07 },
411e4802cb0SJason Chen 	{ 0x6B0D, 0x41 },
412e4802cb0SJason Chen 	{ 0x653D, 0x04 },
413e4802cb0SJason Chen 	{ 0x6B05, 0x8C },
414e4802cb0SJason Chen 	{ 0x6B06, 0xF9 },
415e4802cb0SJason Chen 	{ 0x6B08, 0x65 },
416e4802cb0SJason Chen 	{ 0x6B09, 0xFC },
417e4802cb0SJason Chen 	{ 0x6B0A, 0xCF },
418e4802cb0SJason Chen 	{ 0x6B0B, 0xD2 },
419e4802cb0SJason Chen 	{ 0x6700, 0x0E },
420e4802cb0SJason Chen 	{ 0x6707, 0x0E },
421e4802cb0SJason Chen 	{ 0x9104, 0x00 },
422e4802cb0SJason Chen 	{ 0x4648, 0x7F },
423e4802cb0SJason Chen 	{ 0x7420, 0x00 },
424e4802cb0SJason Chen 	{ 0x7421, 0x1C },
425e4802cb0SJason Chen 	{ 0x7422, 0x00 },
426e4802cb0SJason Chen 	{ 0x7423, 0xD7 },
427e4802cb0SJason Chen 	{ 0x5F04, 0x00 },
428e4802cb0SJason Chen 	{ 0x5F05, 0xED },
429e4802cb0SJason Chen 	{ 0x0112, 0x0A },
430e4802cb0SJason Chen 	{ 0x0113, 0x0A },
431e4802cb0SJason Chen 	{ 0x0114, 0x03 },
432e4802cb0SJason Chen 	{ 0x0342, 0x14 },
433e4802cb0SJason Chen 	{ 0x0343, 0xE8 },
434e4802cb0SJason Chen 	{ 0x0340, 0x03 },
435e4802cb0SJason Chen 	{ 0x0341, 0x4C },
436e4802cb0SJason Chen 	{ 0x0344, 0x00 },
437e4802cb0SJason Chen 	{ 0x0345, 0x00 },
438e4802cb0SJason Chen 	{ 0x0346, 0x00 },
439e4802cb0SJason Chen 	{ 0x0347, 0x00 },
440e4802cb0SJason Chen 	{ 0x0348, 0x10 },
441e4802cb0SJason Chen 	{ 0x0349, 0x6F },
442e4802cb0SJason Chen 	{ 0x034A, 0x0C },
443e4802cb0SJason Chen 	{ 0x034B, 0x2E },
444e4802cb0SJason Chen 	{ 0x0381, 0x01 },
445e4802cb0SJason Chen 	{ 0x0383, 0x01 },
446e4802cb0SJason Chen 	{ 0x0385, 0x01 },
447e4802cb0SJason Chen 	{ 0x0387, 0x01 },
448e4802cb0SJason Chen 	{ 0x0900, 0x01 },
449e4802cb0SJason Chen 	{ 0x0901, 0x14 },
450e4802cb0SJason Chen 	{ 0x0401, 0x01 },
451e4802cb0SJason Chen 	{ 0x0404, 0x00 },
452e4802cb0SJason Chen 	{ 0x0405, 0x40 },
453e4802cb0SJason Chen 	{ 0x0408, 0x00 },
454e4802cb0SJason Chen 	{ 0x0409, 0x06 },
455e4802cb0SJason Chen 	{ 0x040A, 0x00 },
456e4802cb0SJason Chen 	{ 0x040B, 0x00 },
457e4802cb0SJason Chen 	{ 0x040C, 0x10 },
458e4802cb0SJason Chen 	{ 0x040D, 0x64 },
459e4802cb0SJason Chen 	{ 0x040E, 0x03 },
460e4802cb0SJason Chen 	{ 0x040F, 0x0C },
461e4802cb0SJason Chen 	{ 0x3038, 0x00 },
462e4802cb0SJason Chen 	{ 0x303A, 0x00 },
463e4802cb0SJason Chen 	{ 0x303B, 0x10 },
464e4802cb0SJason Chen 	{ 0x300D, 0x00 },
465e4802cb0SJason Chen 	{ 0x034C, 0x04 },
466e4802cb0SJason Chen 	{ 0x034D, 0x18 },
467e4802cb0SJason Chen 	{ 0x034E, 0x03 },
468e4802cb0SJason Chen 	{ 0x034F, 0x0C },
469e4802cb0SJason Chen 	{ 0x0350, 0x01 },
470e4802cb0SJason Chen 	{ 0x0202, 0x03 },
471e4802cb0SJason Chen 	{ 0x0203, 0x42 },
472e4802cb0SJason Chen 	{ 0x0204, 0x00 },
473e4802cb0SJason Chen 	{ 0x0205, 0x00 },
474e4802cb0SJason Chen 	{ 0x020E, 0x01 },
475e4802cb0SJason Chen 	{ 0x020F, 0x00 },
476e4802cb0SJason Chen 	{ 0x0210, 0x01 },
477e4802cb0SJason Chen 	{ 0x0211, 0x00 },
478e4802cb0SJason Chen 	{ 0x0212, 0x01 },
479e4802cb0SJason Chen 	{ 0x0213, 0x00 },
480e4802cb0SJason Chen 	{ 0x0214, 0x01 },
481e4802cb0SJason Chen 	{ 0x0215, 0x00 },
482e4802cb0SJason Chen 	{ 0x7BCD, 0x00 },
483e4802cb0SJason Chen 	{ 0x94DC, 0x20 },
484e4802cb0SJason Chen 	{ 0x94DD, 0x20 },
485e4802cb0SJason Chen 	{ 0x94DE, 0x20 },
486e4802cb0SJason Chen 	{ 0x95DC, 0x20 },
487e4802cb0SJason Chen 	{ 0x95DD, 0x20 },
488e4802cb0SJason Chen 	{ 0x95DE, 0x20 },
489e4802cb0SJason Chen 	{ 0x7FB0, 0x00 },
490e4802cb0SJason Chen 	{ 0x9010, 0x3E },
491e4802cb0SJason Chen 	{ 0x9419, 0x50 },
492e4802cb0SJason Chen 	{ 0x941B, 0x50 },
493e4802cb0SJason Chen 	{ 0x9519, 0x50 },
494e4802cb0SJason Chen 	{ 0x951B, 0x50 },
495e4802cb0SJason Chen 	{ 0x3030, 0x00 },
496e4802cb0SJason Chen 	{ 0x3032, 0x00 },
497e4802cb0SJason Chen 	{ 0x0220, 0x00 },
498e4802cb0SJason Chen };
499e4802cb0SJason Chen 
500e4802cb0SJason Chen static const char * const imx258_test_pattern_menu[] = {
501e4802cb0SJason Chen 	"Disabled",
502ce6ebeacSBingbu Cao 	"Solid Colour",
503ce6ebeacSBingbu Cao 	"Eight Vertical Colour Bars",
504ce6ebeacSBingbu Cao 	"Colour Bars With Fade to Grey",
505ce6ebeacSBingbu Cao 	"Pseudorandom Sequence (PN9)",
506e4802cb0SJason Chen };
507e4802cb0SJason Chen 
508e4802cb0SJason Chen /* Configurations for supported link frequencies */
509e4802cb0SJason Chen #define IMX258_LINK_FREQ_634MHZ	633600000ULL
510e4802cb0SJason Chen #define IMX258_LINK_FREQ_320MHZ	320000000ULL
511e4802cb0SJason Chen 
512e4802cb0SJason Chen enum {
513e4802cb0SJason Chen 	IMX258_LINK_FREQ_1267MBPS,
514e4802cb0SJason Chen 	IMX258_LINK_FREQ_640MBPS,
515e4802cb0SJason Chen };
516e4802cb0SJason Chen 
517e4802cb0SJason Chen /*
518e4802cb0SJason Chen  * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
519e4802cb0SJason Chen  * data rate => double data rate; number of lanes => 4; bits per pixel => 10
520e4802cb0SJason Chen  */
521e4802cb0SJason Chen static u64 link_freq_to_pixel_rate(u64 f)
522e4802cb0SJason Chen {
523e4802cb0SJason Chen 	f *= 2 * 4;
524e4802cb0SJason Chen 	do_div(f, 10);
525e4802cb0SJason Chen 
526e4802cb0SJason Chen 	return f;
527e4802cb0SJason Chen }
528e4802cb0SJason Chen 
529e4802cb0SJason Chen /* Menu items for LINK_FREQ V4L2 control */
530e4802cb0SJason Chen static const s64 link_freq_menu_items[] = {
531e4802cb0SJason Chen 	IMX258_LINK_FREQ_634MHZ,
532e4802cb0SJason Chen 	IMX258_LINK_FREQ_320MHZ,
533e4802cb0SJason Chen };
534e4802cb0SJason Chen 
535e4802cb0SJason Chen /* Link frequency configs */
536e4802cb0SJason Chen static const struct imx258_link_freq_config link_freq_configs[] = {
537e4802cb0SJason Chen 	[IMX258_LINK_FREQ_1267MBPS] = {
538e4802cb0SJason Chen 		.pixels_per_line = IMX258_PPL_DEFAULT,
539e4802cb0SJason Chen 		.reg_list = {
540e4802cb0SJason Chen 			.num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps),
541e4802cb0SJason Chen 			.regs = mipi_data_rate_1267mbps,
542e4802cb0SJason Chen 		}
543e4802cb0SJason Chen 	},
544e4802cb0SJason Chen 	[IMX258_LINK_FREQ_640MBPS] = {
545e4802cb0SJason Chen 		.pixels_per_line = IMX258_PPL_DEFAULT,
546e4802cb0SJason Chen 		.reg_list = {
547e4802cb0SJason Chen 			.num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps),
548e4802cb0SJason Chen 			.regs = mipi_data_rate_640mbps,
549e4802cb0SJason Chen 		}
550e4802cb0SJason Chen 	},
551e4802cb0SJason Chen };
552e4802cb0SJason Chen 
553e4802cb0SJason Chen /* Mode configs */
554e4802cb0SJason Chen static const struct imx258_mode supported_modes[] = {
555e4802cb0SJason Chen 	{
556e4802cb0SJason Chen 		.width = 4208,
557e4802cb0SJason Chen 		.height = 3118,
558e4802cb0SJason Chen 		.vts_def = IMX258_VTS_30FPS,
559e4802cb0SJason Chen 		.vts_min = IMX258_VTS_30FPS,
560e4802cb0SJason Chen 		.reg_list = {
561e4802cb0SJason Chen 			.num_of_regs = ARRAY_SIZE(mode_4208x3118_regs),
562e4802cb0SJason Chen 			.regs = mode_4208x3118_regs,
563e4802cb0SJason Chen 		},
564e4802cb0SJason Chen 		.link_freq_index = IMX258_LINK_FREQ_1267MBPS,
565e4802cb0SJason Chen 	},
566e4802cb0SJason Chen 	{
567e4802cb0SJason Chen 		.width = 2104,
568e4802cb0SJason Chen 		.height = 1560,
569e4802cb0SJason Chen 		.vts_def = IMX258_VTS_30FPS_2K,
570e4802cb0SJason Chen 		.vts_min = IMX258_VTS_30FPS_2K,
571e4802cb0SJason Chen 		.reg_list = {
572e4802cb0SJason Chen 			.num_of_regs = ARRAY_SIZE(mode_2104_1560_regs),
573e4802cb0SJason Chen 			.regs = mode_2104_1560_regs,
574e4802cb0SJason Chen 		},
575e4802cb0SJason Chen 		.link_freq_index = IMX258_LINK_FREQ_640MBPS,
576e4802cb0SJason Chen 	},
577e4802cb0SJason Chen 	{
578e4802cb0SJason Chen 		.width = 1048,
579e4802cb0SJason Chen 		.height = 780,
580e4802cb0SJason Chen 		.vts_def = IMX258_VTS_30FPS_VGA,
581e4802cb0SJason Chen 		.vts_min = IMX258_VTS_30FPS_VGA,
582e4802cb0SJason Chen 		.reg_list = {
583e4802cb0SJason Chen 			.num_of_regs = ARRAY_SIZE(mode_1048_780_regs),
584e4802cb0SJason Chen 			.regs = mode_1048_780_regs,
585e4802cb0SJason Chen 		},
586e4802cb0SJason Chen 		.link_freq_index = IMX258_LINK_FREQ_640MBPS,
587e4802cb0SJason Chen 	},
588e4802cb0SJason Chen };
589e4802cb0SJason Chen 
590e4802cb0SJason Chen struct imx258 {
591e4802cb0SJason Chen 	struct v4l2_subdev sd;
592e4802cb0SJason Chen 	struct media_pad pad;
593e4802cb0SJason Chen 
594e4802cb0SJason Chen 	struct v4l2_ctrl_handler ctrl_handler;
595e4802cb0SJason Chen 	/* V4L2 Controls */
596e4802cb0SJason Chen 	struct v4l2_ctrl *link_freq;
597e4802cb0SJason Chen 	struct v4l2_ctrl *pixel_rate;
598e4802cb0SJason Chen 	struct v4l2_ctrl *vblank;
599e4802cb0SJason Chen 	struct v4l2_ctrl *hblank;
600e4802cb0SJason Chen 	struct v4l2_ctrl *exposure;
601e4802cb0SJason Chen 
602e4802cb0SJason Chen 	/* Current mode */
603e4802cb0SJason Chen 	const struct imx258_mode *cur_mode;
604e4802cb0SJason Chen 
605e4802cb0SJason Chen 	/*
606e4802cb0SJason Chen 	 * Mutex for serialized access:
607e4802cb0SJason Chen 	 * Protect sensor module set pad format and start/stop streaming safely.
608e4802cb0SJason Chen 	 */
609e4802cb0SJason Chen 	struct mutex mutex;
610e4802cb0SJason Chen 
611e4802cb0SJason Chen 	/* Streaming on/off */
612e4802cb0SJason Chen 	bool streaming;
613e4802cb0SJason Chen };
614e4802cb0SJason Chen 
615e4802cb0SJason Chen static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd)
616e4802cb0SJason Chen {
617e4802cb0SJason Chen 	return container_of(_sd, struct imx258, sd);
618e4802cb0SJason Chen }
619e4802cb0SJason Chen 
620e4802cb0SJason Chen /* Read registers up to 2 at a time */
621e4802cb0SJason Chen static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val)
622e4802cb0SJason Chen {
623e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
624e4802cb0SJason Chen 	struct i2c_msg msgs[2];
625e4802cb0SJason Chen 	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
626e4802cb0SJason Chen 	u8 data_buf[4] = { 0, };
627e4802cb0SJason Chen 	int ret;
628e4802cb0SJason Chen 
629e4802cb0SJason Chen 	if (len > 4)
630e4802cb0SJason Chen 		return -EINVAL;
631e4802cb0SJason Chen 
632e4802cb0SJason Chen 	/* Write register address */
633e4802cb0SJason Chen 	msgs[0].addr = client->addr;
634e4802cb0SJason Chen 	msgs[0].flags = 0;
635e4802cb0SJason Chen 	msgs[0].len = ARRAY_SIZE(addr_buf);
636e4802cb0SJason Chen 	msgs[0].buf = addr_buf;
637e4802cb0SJason Chen 
638e4802cb0SJason Chen 	/* Read data from register */
639e4802cb0SJason Chen 	msgs[1].addr = client->addr;
640e4802cb0SJason Chen 	msgs[1].flags = I2C_M_RD;
641e4802cb0SJason Chen 	msgs[1].len = len;
642e4802cb0SJason Chen 	msgs[1].buf = &data_buf[4 - len];
643e4802cb0SJason Chen 
644e4802cb0SJason Chen 	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
645e4802cb0SJason Chen 	if (ret != ARRAY_SIZE(msgs))
646e4802cb0SJason Chen 		return -EIO;
647e4802cb0SJason Chen 
648e4802cb0SJason Chen 	*val = get_unaligned_be32(data_buf);
649e4802cb0SJason Chen 
650e4802cb0SJason Chen 	return 0;
651e4802cb0SJason Chen }
652e4802cb0SJason Chen 
653e4802cb0SJason Chen /* Write registers up to 2 at a time */
654e4802cb0SJason Chen static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val)
655e4802cb0SJason Chen {
656e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
657e4802cb0SJason Chen 	u8 buf[6];
658e4802cb0SJason Chen 
659e4802cb0SJason Chen 	if (len > 4)
660e4802cb0SJason Chen 		return -EINVAL;
661e4802cb0SJason Chen 
662e4802cb0SJason Chen 	put_unaligned_be16(reg, buf);
663e4802cb0SJason Chen 	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
664e4802cb0SJason Chen 	if (i2c_master_send(client, buf, len + 2) != len + 2)
665e4802cb0SJason Chen 		return -EIO;
666e4802cb0SJason Chen 
667e4802cb0SJason Chen 	return 0;
668e4802cb0SJason Chen }
669e4802cb0SJason Chen 
670e4802cb0SJason Chen /* Write a list of registers */
671e4802cb0SJason Chen static int imx258_write_regs(struct imx258 *imx258,
672e4802cb0SJason Chen 			     const struct imx258_reg *regs, u32 len)
673e4802cb0SJason Chen {
674e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
675e4802cb0SJason Chen 	unsigned int i;
676e4802cb0SJason Chen 	int ret;
677e4802cb0SJason Chen 
678e4802cb0SJason Chen 	for (i = 0; i < len; i++) {
679e4802cb0SJason Chen 		ret = imx258_write_reg(imx258, regs[i].address, 1,
680e4802cb0SJason Chen 					regs[i].val);
681e4802cb0SJason Chen 		if (ret) {
682e4802cb0SJason Chen 			dev_err_ratelimited(
683e4802cb0SJason Chen 				&client->dev,
684e4802cb0SJason Chen 				"Failed to write reg 0x%4.4x. error = %d\n",
685e4802cb0SJason Chen 				regs[i].address, ret);
686e4802cb0SJason Chen 
687e4802cb0SJason Chen 			return ret;
688e4802cb0SJason Chen 		}
689e4802cb0SJason Chen 	}
690e4802cb0SJason Chen 
691e4802cb0SJason Chen 	return 0;
692e4802cb0SJason Chen }
693e4802cb0SJason Chen 
694e4802cb0SJason Chen /* Open sub-device */
695e4802cb0SJason Chen static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
696e4802cb0SJason Chen {
697e4802cb0SJason Chen 	struct v4l2_mbus_framefmt *try_fmt =
698e4802cb0SJason Chen 		v4l2_subdev_get_try_format(sd, fh->pad, 0);
699e4802cb0SJason Chen 
700e4802cb0SJason Chen 	/* Initialize try_fmt */
701e4802cb0SJason Chen 	try_fmt->width = supported_modes[0].width;
702e4802cb0SJason Chen 	try_fmt->height = supported_modes[0].height;
703e4802cb0SJason Chen 	try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
704e4802cb0SJason Chen 	try_fmt->field = V4L2_FIELD_NONE;
705e4802cb0SJason Chen 
706e4802cb0SJason Chen 	return 0;
707e4802cb0SJason Chen }
708e4802cb0SJason Chen 
709e4802cb0SJason Chen static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val)
710e4802cb0SJason Chen {
711e4802cb0SJason Chen 	int ret;
712e4802cb0SJason Chen 
713e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN,
714e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
715e4802cb0SJason Chen 				val);
716e4802cb0SJason Chen 	if (ret)
717e4802cb0SJason Chen 		return ret;
718e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN,
719e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
720e4802cb0SJason Chen 				val);
721e4802cb0SJason Chen 	if (ret)
722e4802cb0SJason Chen 		return ret;
723e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN,
724e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
725e4802cb0SJason Chen 				val);
726e4802cb0SJason Chen 	if (ret)
727e4802cb0SJason Chen 		return ret;
728e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN,
729e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
730e4802cb0SJason Chen 				val);
731e4802cb0SJason Chen 	if (ret)
732e4802cb0SJason Chen 		return ret;
733e4802cb0SJason Chen 	return 0;
734e4802cb0SJason Chen }
735e4802cb0SJason Chen 
736e4802cb0SJason Chen static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
737e4802cb0SJason Chen {
738e4802cb0SJason Chen 	struct imx258 *imx258 =
739e4802cb0SJason Chen 		container_of(ctrl->handler, struct imx258, ctrl_handler);
740e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
741e4802cb0SJason Chen 	int ret = 0;
742e4802cb0SJason Chen 
743e4802cb0SJason Chen 	/*
744e4802cb0SJason Chen 	 * Applying V4L2 control value only happens
745e4802cb0SJason Chen 	 * when power is up for streaming
746e4802cb0SJason Chen 	 */
747e4802cb0SJason Chen 	if (pm_runtime_get_if_in_use(&client->dev) == 0)
748e4802cb0SJason Chen 		return 0;
749e4802cb0SJason Chen 
750e4802cb0SJason Chen 	switch (ctrl->id) {
751e4802cb0SJason Chen 	case V4L2_CID_ANALOGUE_GAIN:
752e4802cb0SJason Chen 		ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN,
753e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
754e4802cb0SJason Chen 				ctrl->val);
755e4802cb0SJason Chen 		break;
756e4802cb0SJason Chen 	case V4L2_CID_EXPOSURE:
757e4802cb0SJason Chen 		ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE,
758e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
759e4802cb0SJason Chen 				ctrl->val);
760e4802cb0SJason Chen 		break;
761e4802cb0SJason Chen 	case V4L2_CID_DIGITAL_GAIN:
762e4802cb0SJason Chen 		ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT,
763e4802cb0SJason Chen 				ctrl->val);
764e4802cb0SJason Chen 		break;
765e4802cb0SJason Chen 	case V4L2_CID_TEST_PATTERN:
766e4802cb0SJason Chen 		ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
767e4802cb0SJason Chen 				IMX258_REG_VALUE_16BIT,
76853f6f81dSChen, JasonX Z 				ctrl->val);
769e4802cb0SJason Chen 		ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
770e4802cb0SJason Chen 				IMX258_REG_VALUE_08BIT,
77153f6f81dSChen, JasonX Z 				!ctrl->val ? REG_CONFIG_MIRROR_FLIP :
772e4802cb0SJason Chen 				REG_CONFIG_FLIP_TEST_PATTERN);
773e4802cb0SJason Chen 		break;
774e4802cb0SJason Chen 	default:
775e4802cb0SJason Chen 		dev_info(&client->dev,
776e4802cb0SJason Chen 			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
777e4802cb0SJason Chen 			 ctrl->id, ctrl->val);
778e4802cb0SJason Chen 		ret = -EINVAL;
779e4802cb0SJason Chen 		break;
780e4802cb0SJason Chen 	}
781e4802cb0SJason Chen 
782e4802cb0SJason Chen 	pm_runtime_put(&client->dev);
783e4802cb0SJason Chen 
784e4802cb0SJason Chen 	return ret;
785e4802cb0SJason Chen }
786e4802cb0SJason Chen 
787e4802cb0SJason Chen static const struct v4l2_ctrl_ops imx258_ctrl_ops = {
788e4802cb0SJason Chen 	.s_ctrl = imx258_set_ctrl,
789e4802cb0SJason Chen };
790e4802cb0SJason Chen 
791e4802cb0SJason Chen static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
792e4802cb0SJason Chen 				  struct v4l2_subdev_pad_config *cfg,
793e4802cb0SJason Chen 				  struct v4l2_subdev_mbus_code_enum *code)
794e4802cb0SJason Chen {
795e4802cb0SJason Chen 	/* Only one bayer order(GRBG) is supported */
796e4802cb0SJason Chen 	if (code->index > 0)
797e4802cb0SJason Chen 		return -EINVAL;
798e4802cb0SJason Chen 
799e4802cb0SJason Chen 	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
800e4802cb0SJason Chen 
801e4802cb0SJason Chen 	return 0;
802e4802cb0SJason Chen }
803e4802cb0SJason Chen 
804e4802cb0SJason Chen static int imx258_enum_frame_size(struct v4l2_subdev *sd,
805e4802cb0SJason Chen 				  struct v4l2_subdev_pad_config *cfg,
806e4802cb0SJason Chen 				  struct v4l2_subdev_frame_size_enum *fse)
807e4802cb0SJason Chen {
808e4802cb0SJason Chen 	if (fse->index >= ARRAY_SIZE(supported_modes))
809e4802cb0SJason Chen 		return -EINVAL;
810e4802cb0SJason Chen 
811e4802cb0SJason Chen 	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
812e4802cb0SJason Chen 		return -EINVAL;
813e4802cb0SJason Chen 
814e4802cb0SJason Chen 	fse->min_width = supported_modes[fse->index].width;
815e4802cb0SJason Chen 	fse->max_width = fse->min_width;
816e4802cb0SJason Chen 	fse->min_height = supported_modes[fse->index].height;
817e4802cb0SJason Chen 	fse->max_height = fse->min_height;
818e4802cb0SJason Chen 
819e4802cb0SJason Chen 	return 0;
820e4802cb0SJason Chen }
821e4802cb0SJason Chen 
822e4802cb0SJason Chen static void imx258_update_pad_format(const struct imx258_mode *mode,
823e4802cb0SJason Chen 				     struct v4l2_subdev_format *fmt)
824e4802cb0SJason Chen {
825e4802cb0SJason Chen 	fmt->format.width = mode->width;
826e4802cb0SJason Chen 	fmt->format.height = mode->height;
827e4802cb0SJason Chen 	fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
828e4802cb0SJason Chen 	fmt->format.field = V4L2_FIELD_NONE;
829e4802cb0SJason Chen }
830e4802cb0SJason Chen 
831e4802cb0SJason Chen static int __imx258_get_pad_format(struct imx258 *imx258,
832e4802cb0SJason Chen 				   struct v4l2_subdev_pad_config *cfg,
833e4802cb0SJason Chen 				   struct v4l2_subdev_format *fmt)
834e4802cb0SJason Chen {
835e4802cb0SJason Chen 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
836e4802cb0SJason Chen 		fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg,
837e4802cb0SJason Chen 							  fmt->pad);
838e4802cb0SJason Chen 	else
839e4802cb0SJason Chen 		imx258_update_pad_format(imx258->cur_mode, fmt);
840e4802cb0SJason Chen 
841e4802cb0SJason Chen 	return 0;
842e4802cb0SJason Chen }
843e4802cb0SJason Chen 
844e4802cb0SJason Chen static int imx258_get_pad_format(struct v4l2_subdev *sd,
845e4802cb0SJason Chen 				 struct v4l2_subdev_pad_config *cfg,
846e4802cb0SJason Chen 				 struct v4l2_subdev_format *fmt)
847e4802cb0SJason Chen {
848e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
849e4802cb0SJason Chen 	int ret;
850e4802cb0SJason Chen 
851e4802cb0SJason Chen 	mutex_lock(&imx258->mutex);
852e4802cb0SJason Chen 	ret = __imx258_get_pad_format(imx258, cfg, fmt);
853e4802cb0SJason Chen 	mutex_unlock(&imx258->mutex);
854e4802cb0SJason Chen 
855e4802cb0SJason Chen 	return ret;
856e4802cb0SJason Chen }
857e4802cb0SJason Chen 
858e4802cb0SJason Chen static int imx258_set_pad_format(struct v4l2_subdev *sd,
859e4802cb0SJason Chen 				 struct v4l2_subdev_pad_config *cfg,
860e4802cb0SJason Chen 				 struct v4l2_subdev_format *fmt)
861e4802cb0SJason Chen {
862e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
863e4802cb0SJason Chen 	const struct imx258_mode *mode;
864e4802cb0SJason Chen 	struct v4l2_mbus_framefmt *framefmt;
865e4802cb0SJason Chen 	s32 vblank_def;
866e4802cb0SJason Chen 	s32 vblank_min;
867e4802cb0SJason Chen 	s64 h_blank;
868e4802cb0SJason Chen 	s64 pixel_rate;
869e4802cb0SJason Chen 	s64 link_freq;
870e4802cb0SJason Chen 
871e4802cb0SJason Chen 	mutex_lock(&imx258->mutex);
872e4802cb0SJason Chen 
873e4802cb0SJason Chen 	/* Only one raw bayer(GBRG) order is supported */
874e4802cb0SJason Chen 	fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
875e4802cb0SJason Chen 
876e4802cb0SJason Chen 	mode = v4l2_find_nearest_size(supported_modes,
877e4802cb0SJason Chen 		ARRAY_SIZE(supported_modes), width, height,
878e4802cb0SJason Chen 		fmt->format.width, fmt->format.height);
879e4802cb0SJason Chen 	imx258_update_pad_format(mode, fmt);
880e4802cb0SJason Chen 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
881e4802cb0SJason Chen 		framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
882e4802cb0SJason Chen 		*framefmt = fmt->format;
883e4802cb0SJason Chen 	} else {
884e4802cb0SJason Chen 		imx258->cur_mode = mode;
885e4802cb0SJason Chen 		__v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
886e4802cb0SJason Chen 
887e4802cb0SJason Chen 		link_freq = link_freq_menu_items[mode->link_freq_index];
888e4802cb0SJason Chen 		pixel_rate = link_freq_to_pixel_rate(link_freq);
889e4802cb0SJason Chen 		__v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
890e4802cb0SJason Chen 		/* Update limits and set FPS to default */
891e4802cb0SJason Chen 		vblank_def = imx258->cur_mode->vts_def -
892e4802cb0SJason Chen 			     imx258->cur_mode->height;
893e4802cb0SJason Chen 		vblank_min = imx258->cur_mode->vts_min -
894e4802cb0SJason Chen 			     imx258->cur_mode->height;
895e4802cb0SJason Chen 		__v4l2_ctrl_modify_range(
896e4802cb0SJason Chen 			imx258->vblank, vblank_min,
897e4802cb0SJason Chen 			IMX258_VTS_MAX - imx258->cur_mode->height, 1,
898e4802cb0SJason Chen 			vblank_def);
899e4802cb0SJason Chen 		__v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def);
900e4802cb0SJason Chen 		h_blank =
901e4802cb0SJason Chen 			link_freq_configs[mode->link_freq_index].pixels_per_line
902e4802cb0SJason Chen 			 - imx258->cur_mode->width;
903e4802cb0SJason Chen 		__v4l2_ctrl_modify_range(imx258->hblank, h_blank,
904e4802cb0SJason Chen 					 h_blank, 1, h_blank);
905e4802cb0SJason Chen 	}
906e4802cb0SJason Chen 
907e4802cb0SJason Chen 	mutex_unlock(&imx258->mutex);
908e4802cb0SJason Chen 
909e4802cb0SJason Chen 	return 0;
910e4802cb0SJason Chen }
911e4802cb0SJason Chen 
912e4802cb0SJason Chen /* Start streaming */
913e4802cb0SJason Chen static int imx258_start_streaming(struct imx258 *imx258)
914e4802cb0SJason Chen {
915e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
916e4802cb0SJason Chen 	const struct imx258_reg_list *reg_list;
917e4802cb0SJason Chen 	int ret, link_freq_index;
918e4802cb0SJason Chen 
919e4802cb0SJason Chen 	/* Setup PLL */
920e4802cb0SJason Chen 	link_freq_index = imx258->cur_mode->link_freq_index;
921e4802cb0SJason Chen 	reg_list = &link_freq_configs[link_freq_index].reg_list;
922e4802cb0SJason Chen 	ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
923e4802cb0SJason Chen 	if (ret) {
924e4802cb0SJason Chen 		dev_err(&client->dev, "%s failed to set plls\n", __func__);
925e4802cb0SJason Chen 		return ret;
926e4802cb0SJason Chen 	}
927e4802cb0SJason Chen 
928e4802cb0SJason Chen 	/* Apply default values of current mode */
929e4802cb0SJason Chen 	reg_list = &imx258->cur_mode->reg_list;
930e4802cb0SJason Chen 	ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
931e4802cb0SJason Chen 	if (ret) {
932e4802cb0SJason Chen 		dev_err(&client->dev, "%s failed to set mode\n", __func__);
933e4802cb0SJason Chen 		return ret;
934e4802cb0SJason Chen 	}
935e4802cb0SJason Chen 
936e4802cb0SJason Chen 	/* Set Orientation be 180 degree */
937e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
938e4802cb0SJason Chen 			       IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
939e4802cb0SJason Chen 	if (ret) {
940e4802cb0SJason Chen 		dev_err(&client->dev, "%s failed to set orientation\n",
941e4802cb0SJason Chen 			__func__);
942e4802cb0SJason Chen 		return ret;
943e4802cb0SJason Chen 	}
944e4802cb0SJason Chen 
945e4802cb0SJason Chen 	/* Apply customized values from user */
946e4802cb0SJason Chen 	ret =  __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
947e4802cb0SJason Chen 	if (ret)
948e4802cb0SJason Chen 		return ret;
949e4802cb0SJason Chen 
950e4802cb0SJason Chen 	/* set stream on register */
951e4802cb0SJason Chen 	return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT,
952e4802cb0SJason Chen 				IMX258_REG_VALUE_08BIT,
953e4802cb0SJason Chen 				IMX258_MODE_STREAMING);
954e4802cb0SJason Chen }
955e4802cb0SJason Chen 
956e4802cb0SJason Chen /* Stop streaming */
957e4802cb0SJason Chen static int imx258_stop_streaming(struct imx258 *imx258)
958e4802cb0SJason Chen {
959e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
960e4802cb0SJason Chen 	int ret;
961e4802cb0SJason Chen 
962e4802cb0SJason Chen 	/* set stream off register */
963e4802cb0SJason Chen 	ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT,
964e4802cb0SJason Chen 		IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY);
965e4802cb0SJason Chen 	if (ret)
966e4802cb0SJason Chen 		dev_err(&client->dev, "%s failed to set stream\n", __func__);
967e4802cb0SJason Chen 
968e4802cb0SJason Chen 	/*
969e4802cb0SJason Chen 	 * Return success even if it was an error, as there is nothing the
970e4802cb0SJason Chen 	 * caller can do about it.
971e4802cb0SJason Chen 	 */
972e4802cb0SJason Chen 	return 0;
973e4802cb0SJason Chen }
974e4802cb0SJason Chen 
975e4802cb0SJason Chen static int imx258_set_stream(struct v4l2_subdev *sd, int enable)
976e4802cb0SJason Chen {
977e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
978e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(sd);
979e4802cb0SJason Chen 	int ret = 0;
980e4802cb0SJason Chen 
981e4802cb0SJason Chen 	mutex_lock(&imx258->mutex);
982e4802cb0SJason Chen 	if (imx258->streaming == enable) {
983e4802cb0SJason Chen 		mutex_unlock(&imx258->mutex);
984e4802cb0SJason Chen 		return 0;
985e4802cb0SJason Chen 	}
986e4802cb0SJason Chen 
987e4802cb0SJason Chen 	if (enable) {
988e4802cb0SJason Chen 		ret = pm_runtime_get_sync(&client->dev);
989e4802cb0SJason Chen 		if (ret < 0) {
990e4802cb0SJason Chen 			pm_runtime_put_noidle(&client->dev);
991e4802cb0SJason Chen 			goto err_unlock;
992e4802cb0SJason Chen 		}
993e4802cb0SJason Chen 
994e4802cb0SJason Chen 		/*
995e4802cb0SJason Chen 		 * Apply default & customized values
996e4802cb0SJason Chen 		 * and then start streaming.
997e4802cb0SJason Chen 		 */
998e4802cb0SJason Chen 		ret = imx258_start_streaming(imx258);
999e4802cb0SJason Chen 		if (ret)
1000e4802cb0SJason Chen 			goto err_rpm_put;
1001e4802cb0SJason Chen 	} else {
1002e4802cb0SJason Chen 		imx258_stop_streaming(imx258);
1003e4802cb0SJason Chen 		pm_runtime_put(&client->dev);
1004e4802cb0SJason Chen 	}
1005e4802cb0SJason Chen 
1006e4802cb0SJason Chen 	imx258->streaming = enable;
1007e4802cb0SJason Chen 	mutex_unlock(&imx258->mutex);
1008e4802cb0SJason Chen 
1009e4802cb0SJason Chen 	return ret;
1010e4802cb0SJason Chen 
1011e4802cb0SJason Chen err_rpm_put:
1012e4802cb0SJason Chen 	pm_runtime_put(&client->dev);
1013e4802cb0SJason Chen err_unlock:
1014e4802cb0SJason Chen 	mutex_unlock(&imx258->mutex);
1015e4802cb0SJason Chen 
1016e4802cb0SJason Chen 	return ret;
1017e4802cb0SJason Chen }
1018e4802cb0SJason Chen 
1019e4802cb0SJason Chen static int __maybe_unused imx258_suspend(struct device *dev)
1020e4802cb0SJason Chen {
1021e4802cb0SJason Chen 	struct i2c_client *client = to_i2c_client(dev);
1022e4802cb0SJason Chen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1023e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
1024e4802cb0SJason Chen 
1025e4802cb0SJason Chen 	if (imx258->streaming)
1026e4802cb0SJason Chen 		imx258_stop_streaming(imx258);
1027e4802cb0SJason Chen 
1028e4802cb0SJason Chen 	return 0;
1029e4802cb0SJason Chen }
1030e4802cb0SJason Chen 
1031e4802cb0SJason Chen static int __maybe_unused imx258_resume(struct device *dev)
1032e4802cb0SJason Chen {
1033e4802cb0SJason Chen 	struct i2c_client *client = to_i2c_client(dev);
1034e4802cb0SJason Chen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1035e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
1036e4802cb0SJason Chen 	int ret;
1037e4802cb0SJason Chen 
1038e4802cb0SJason Chen 	if (imx258->streaming) {
1039e4802cb0SJason Chen 		ret = imx258_start_streaming(imx258);
1040e4802cb0SJason Chen 		if (ret)
1041e4802cb0SJason Chen 			goto error;
1042e4802cb0SJason Chen 	}
1043e4802cb0SJason Chen 
1044e4802cb0SJason Chen 	return 0;
1045e4802cb0SJason Chen 
1046e4802cb0SJason Chen error:
1047e4802cb0SJason Chen 	imx258_stop_streaming(imx258);
1048e4802cb0SJason Chen 	imx258->streaming = 0;
1049e4802cb0SJason Chen 	return ret;
1050e4802cb0SJason Chen }
1051e4802cb0SJason Chen 
1052e4802cb0SJason Chen /* Verify chip ID */
1053e4802cb0SJason Chen static int imx258_identify_module(struct imx258 *imx258)
1054e4802cb0SJason Chen {
1055e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
1056e4802cb0SJason Chen 	int ret;
1057e4802cb0SJason Chen 	u32 val;
1058e4802cb0SJason Chen 
1059e4802cb0SJason Chen 	ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID,
1060e4802cb0SJason Chen 			      IMX258_REG_VALUE_16BIT, &val);
1061e4802cb0SJason Chen 	if (ret) {
1062e4802cb0SJason Chen 		dev_err(&client->dev, "failed to read chip id %x\n",
1063e4802cb0SJason Chen 			IMX258_CHIP_ID);
1064e4802cb0SJason Chen 		return ret;
1065e4802cb0SJason Chen 	}
1066e4802cb0SJason Chen 
1067e4802cb0SJason Chen 	if (val != IMX258_CHIP_ID) {
1068e4802cb0SJason Chen 		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
1069e4802cb0SJason Chen 			IMX258_CHIP_ID, val);
1070e4802cb0SJason Chen 		return -EIO;
1071e4802cb0SJason Chen 	}
1072e4802cb0SJason Chen 
1073e4802cb0SJason Chen 	return 0;
1074e4802cb0SJason Chen }
1075e4802cb0SJason Chen 
1076e4802cb0SJason Chen static const struct v4l2_subdev_video_ops imx258_video_ops = {
1077e4802cb0SJason Chen 	.s_stream = imx258_set_stream,
1078e4802cb0SJason Chen };
1079e4802cb0SJason Chen 
1080e4802cb0SJason Chen static const struct v4l2_subdev_pad_ops imx258_pad_ops = {
1081e4802cb0SJason Chen 	.enum_mbus_code = imx258_enum_mbus_code,
1082e4802cb0SJason Chen 	.get_fmt = imx258_get_pad_format,
1083e4802cb0SJason Chen 	.set_fmt = imx258_set_pad_format,
1084e4802cb0SJason Chen 	.enum_frame_size = imx258_enum_frame_size,
1085e4802cb0SJason Chen };
1086e4802cb0SJason Chen 
1087e4802cb0SJason Chen static const struct v4l2_subdev_ops imx258_subdev_ops = {
1088e4802cb0SJason Chen 	.video = &imx258_video_ops,
1089e4802cb0SJason Chen 	.pad = &imx258_pad_ops,
1090e4802cb0SJason Chen };
1091e4802cb0SJason Chen 
1092e4802cb0SJason Chen static const struct v4l2_subdev_internal_ops imx258_internal_ops = {
1093e4802cb0SJason Chen 	.open = imx258_open,
1094e4802cb0SJason Chen };
1095e4802cb0SJason Chen 
1096e4802cb0SJason Chen /* Initialize control handlers */
1097e4802cb0SJason Chen static int imx258_init_controls(struct imx258 *imx258)
1098e4802cb0SJason Chen {
1099e4802cb0SJason Chen 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
1100e4802cb0SJason Chen 	struct v4l2_ctrl_handler *ctrl_hdlr;
1101e4802cb0SJason Chen 	s64 vblank_def;
1102e4802cb0SJason Chen 	s64 vblank_min;
1103e4802cb0SJason Chen 	s64 pixel_rate_min;
1104e4802cb0SJason Chen 	s64 pixel_rate_max;
1105e4802cb0SJason Chen 	int ret;
1106e4802cb0SJason Chen 
1107e4802cb0SJason Chen 	ctrl_hdlr = &imx258->ctrl_handler;
1108e4802cb0SJason Chen 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
1109e4802cb0SJason Chen 	if (ret)
1110e4802cb0SJason Chen 		return ret;
1111e4802cb0SJason Chen 
1112e4802cb0SJason Chen 	mutex_init(&imx258->mutex);
1113e4802cb0SJason Chen 	ctrl_hdlr->lock = &imx258->mutex;
1114e4802cb0SJason Chen 	imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
1115e4802cb0SJason Chen 				&imx258_ctrl_ops,
1116e4802cb0SJason Chen 				V4L2_CID_LINK_FREQ,
1117e4802cb0SJason Chen 				ARRAY_SIZE(link_freq_menu_items) - 1,
1118e4802cb0SJason Chen 				0,
1119e4802cb0SJason Chen 				link_freq_menu_items);
1120e4802cb0SJason Chen 
1121e4802cb0SJason Chen 	if (imx258->link_freq)
1122e4802cb0SJason Chen 		imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1123e4802cb0SJason Chen 
1124e4802cb0SJason Chen 	pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]);
1125e4802cb0SJason Chen 	pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]);
1126e4802cb0SJason Chen 	/* By default, PIXEL_RATE is read only */
1127e4802cb0SJason Chen 	imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
1128e4802cb0SJason Chen 				V4L2_CID_PIXEL_RATE,
1129e4802cb0SJason Chen 				pixel_rate_min, pixel_rate_max,
1130e4802cb0SJason Chen 				1, pixel_rate_max);
1131e4802cb0SJason Chen 
1132e4802cb0SJason Chen 
1133e4802cb0SJason Chen 	vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height;
1134e4802cb0SJason Chen 	vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height;
1135e4802cb0SJason Chen 	imx258->vblank = v4l2_ctrl_new_std(
1136e4802cb0SJason Chen 				ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK,
1137e4802cb0SJason Chen 				vblank_min,
1138e4802cb0SJason Chen 				IMX258_VTS_MAX - imx258->cur_mode->height, 1,
1139e4802cb0SJason Chen 				vblank_def);
1140e4802cb0SJason Chen 
1141e4802cb0SJason Chen 	if (imx258->vblank)
1142e4802cb0SJason Chen 		imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1143e4802cb0SJason Chen 
1144e4802cb0SJason Chen 	imx258->hblank = v4l2_ctrl_new_std(
1145e4802cb0SJason Chen 				ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK,
1146e4802cb0SJason Chen 				IMX258_PPL_DEFAULT - imx258->cur_mode->width,
1147e4802cb0SJason Chen 				IMX258_PPL_DEFAULT - imx258->cur_mode->width,
1148e4802cb0SJason Chen 				1,
1149e4802cb0SJason Chen 				IMX258_PPL_DEFAULT - imx258->cur_mode->width);
1150e4802cb0SJason Chen 
1151e4802cb0SJason Chen 	if (imx258->hblank)
1152e4802cb0SJason Chen 		imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1153e4802cb0SJason Chen 
1154e4802cb0SJason Chen 	imx258->exposure = v4l2_ctrl_new_std(
1155e4802cb0SJason Chen 				ctrl_hdlr, &imx258_ctrl_ops,
1156e4802cb0SJason Chen 				V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN,
1157e4802cb0SJason Chen 				IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP,
1158e4802cb0SJason Chen 				IMX258_EXPOSURE_DEFAULT);
1159e4802cb0SJason Chen 
1160e4802cb0SJason Chen 	v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
1161e4802cb0SJason Chen 				IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX,
1162e4802cb0SJason Chen 				IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT);
1163e4802cb0SJason Chen 
1164e4802cb0SJason Chen 	v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
1165e4802cb0SJason Chen 				IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX,
1166e4802cb0SJason Chen 				IMX258_DGTL_GAIN_STEP,
1167e4802cb0SJason Chen 				IMX258_DGTL_GAIN_DEFAULT);
1168e4802cb0SJason Chen 
1169e4802cb0SJason Chen 	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops,
1170e4802cb0SJason Chen 				V4L2_CID_TEST_PATTERN,
1171e4802cb0SJason Chen 				ARRAY_SIZE(imx258_test_pattern_menu) - 1,
1172e4802cb0SJason Chen 				0, 0, imx258_test_pattern_menu);
1173e4802cb0SJason Chen 
1174e4802cb0SJason Chen 	if (ctrl_hdlr->error) {
1175e4802cb0SJason Chen 		ret = ctrl_hdlr->error;
1176e4802cb0SJason Chen 		dev_err(&client->dev, "%s control init failed (%d)\n",
1177e4802cb0SJason Chen 				__func__, ret);
1178e4802cb0SJason Chen 		goto error;
1179e4802cb0SJason Chen 	}
1180e4802cb0SJason Chen 
1181e4802cb0SJason Chen 	imx258->sd.ctrl_handler = ctrl_hdlr;
1182e4802cb0SJason Chen 
1183e4802cb0SJason Chen 	return 0;
1184e4802cb0SJason Chen 
1185e4802cb0SJason Chen error:
1186e4802cb0SJason Chen 	v4l2_ctrl_handler_free(ctrl_hdlr);
1187e4802cb0SJason Chen 	mutex_destroy(&imx258->mutex);
1188e4802cb0SJason Chen 
1189e4802cb0SJason Chen 	return ret;
1190e4802cb0SJason Chen }
1191e4802cb0SJason Chen 
1192e4802cb0SJason Chen static void imx258_free_controls(struct imx258 *imx258)
1193e4802cb0SJason Chen {
1194e4802cb0SJason Chen 	v4l2_ctrl_handler_free(imx258->sd.ctrl_handler);
1195e4802cb0SJason Chen 	mutex_destroy(&imx258->mutex);
1196e4802cb0SJason Chen }
1197e4802cb0SJason Chen 
1198e4802cb0SJason Chen static int imx258_probe(struct i2c_client *client)
1199e4802cb0SJason Chen {
1200e4802cb0SJason Chen 	struct imx258 *imx258;
1201e4802cb0SJason Chen 	int ret;
1202e4802cb0SJason Chen 	u32 val = 0;
1203e4802cb0SJason Chen 
1204e4802cb0SJason Chen 	device_property_read_u32(&client->dev, "clock-frequency", &val);
1205e4802cb0SJason Chen 	if (val != 19200000)
1206e4802cb0SJason Chen 		return -EINVAL;
1207e4802cb0SJason Chen 
120817121d12SSakari Ailus 	/*
120917121d12SSakari Ailus 	 * Check that the device is mounted upside down. The driver only
121017121d12SSakari Ailus 	 * supports a single pixel order right now.
121117121d12SSakari Ailus 	 */
121217121d12SSakari Ailus 	ret = device_property_read_u32(&client->dev, "rotation", &val);
121317121d12SSakari Ailus 	if (ret || val != 180)
121417121d12SSakari Ailus 		return -EINVAL;
121517121d12SSakari Ailus 
1216e4802cb0SJason Chen 	imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL);
1217e4802cb0SJason Chen 	if (!imx258)
1218e4802cb0SJason Chen 		return -ENOMEM;
1219e4802cb0SJason Chen 
1220e4802cb0SJason Chen 	/* Initialize subdev */
1221e4802cb0SJason Chen 	v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
1222e4802cb0SJason Chen 
1223e4802cb0SJason Chen 	/* Check module identity */
1224e4802cb0SJason Chen 	ret = imx258_identify_module(imx258);
1225e4802cb0SJason Chen 	if (ret)
1226e4802cb0SJason Chen 		return ret;
1227e4802cb0SJason Chen 
1228e4802cb0SJason Chen 	/* Set default mode to max resolution */
1229e4802cb0SJason Chen 	imx258->cur_mode = &supported_modes[0];
1230e4802cb0SJason Chen 
1231e4802cb0SJason Chen 	ret = imx258_init_controls(imx258);
1232e4802cb0SJason Chen 	if (ret)
1233e4802cb0SJason Chen 		return ret;
1234e4802cb0SJason Chen 
1235e4802cb0SJason Chen 	/* Initialize subdev */
1236e4802cb0SJason Chen 	imx258->sd.internal_ops = &imx258_internal_ops;
1237e4802cb0SJason Chen 	imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1238e4802cb0SJason Chen 	imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1239e4802cb0SJason Chen 
1240e4802cb0SJason Chen 	/* Initialize source pad */
1241e4802cb0SJason Chen 	imx258->pad.flags = MEDIA_PAD_FL_SOURCE;
1242e4802cb0SJason Chen 
1243e4802cb0SJason Chen 	ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad);
1244e4802cb0SJason Chen 	if (ret)
1245e4802cb0SJason Chen 		goto error_handler_free;
1246e4802cb0SJason Chen 
1247e4802cb0SJason Chen 	ret = v4l2_async_register_subdev_sensor_common(&imx258->sd);
1248e4802cb0SJason Chen 	if (ret < 0)
1249e4802cb0SJason Chen 		goto error_media_entity;
1250e4802cb0SJason Chen 
1251e4802cb0SJason Chen 	pm_runtime_set_active(&client->dev);
1252e4802cb0SJason Chen 	pm_runtime_enable(&client->dev);
1253e4802cb0SJason Chen 	pm_runtime_idle(&client->dev);
1254e4802cb0SJason Chen 
1255e4802cb0SJason Chen 	return 0;
1256e4802cb0SJason Chen 
1257e4802cb0SJason Chen error_media_entity:
1258e4802cb0SJason Chen 	media_entity_cleanup(&imx258->sd.entity);
1259e4802cb0SJason Chen 
1260e4802cb0SJason Chen error_handler_free:
1261e4802cb0SJason Chen 	imx258_free_controls(imx258);
1262e4802cb0SJason Chen 
1263e4802cb0SJason Chen 	return ret;
1264e4802cb0SJason Chen }
1265e4802cb0SJason Chen 
1266e4802cb0SJason Chen static int imx258_remove(struct i2c_client *client)
1267e4802cb0SJason Chen {
1268e4802cb0SJason Chen 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1269e4802cb0SJason Chen 	struct imx258 *imx258 = to_imx258(sd);
1270e4802cb0SJason Chen 
1271e4802cb0SJason Chen 	v4l2_async_unregister_subdev(sd);
1272e4802cb0SJason Chen 	media_entity_cleanup(&sd->entity);
1273e4802cb0SJason Chen 	imx258_free_controls(imx258);
1274e4802cb0SJason Chen 
1275e4802cb0SJason Chen 	pm_runtime_disable(&client->dev);
1276e4802cb0SJason Chen 	pm_runtime_set_suspended(&client->dev);
1277e4802cb0SJason Chen 
1278e4802cb0SJason Chen 	return 0;
1279e4802cb0SJason Chen }
1280e4802cb0SJason Chen 
1281e4802cb0SJason Chen static const struct dev_pm_ops imx258_pm_ops = {
1282e4802cb0SJason Chen 	SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume)
1283e4802cb0SJason Chen };
1284e4802cb0SJason Chen 
1285e4802cb0SJason Chen #ifdef CONFIG_ACPI
1286e4802cb0SJason Chen static const struct acpi_device_id imx258_acpi_ids[] = {
1287e4802cb0SJason Chen 	{ "SONY258A" },
1288e4802cb0SJason Chen 	{ /* sentinel */ }
1289e4802cb0SJason Chen };
1290e4802cb0SJason Chen 
1291e4802cb0SJason Chen MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids);
1292e4802cb0SJason Chen #endif
1293e4802cb0SJason Chen 
1294e4802cb0SJason Chen static struct i2c_driver imx258_i2c_driver = {
1295e4802cb0SJason Chen 	.driver = {
1296e4802cb0SJason Chen 		.name = "imx258",
1297e4802cb0SJason Chen 		.pm = &imx258_pm_ops,
1298e4802cb0SJason Chen 		.acpi_match_table = ACPI_PTR(imx258_acpi_ids),
1299e4802cb0SJason Chen 	},
1300e4802cb0SJason Chen 	.probe_new = imx258_probe,
1301e4802cb0SJason Chen 	.remove = imx258_remove,
1302e4802cb0SJason Chen };
1303e4802cb0SJason Chen 
1304e4802cb0SJason Chen module_i2c_driver(imx258_i2c_driver);
1305e4802cb0SJason Chen 
1306e4802cb0SJason Chen MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>");
13072f248f7fSSakari Ailus MODULE_AUTHOR("Chiang, Alan");
1308*d30ac9d8SSakari Ailus MODULE_AUTHOR("Chen, Jason");
1309e4802cb0SJason Chen MODULE_DESCRIPTION("Sony IMX258 sensor driver");
1310e4802cb0SJason Chen MODULE_LICENSE("GPL v2");
1311