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