1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2022 Intel Corporation. |
3 | |
4 | #include <asm-generic/unaligned.h> |
5 | #include <linux/acpi.h> |
6 | #include <linux/i2c.h> |
7 | #include <linux/module.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <media/v4l2-ctrls.h> |
11 | #include <media/v4l2-device.h> |
12 | #include <media/v4l2-fwnode.h> |
13 | |
14 | #define OV08X40_REG_VALUE_08BIT 1 |
15 | #define OV08X40_REG_VALUE_16BIT 2 |
16 | #define OV08X40_REG_VALUE_24BIT 3 |
17 | |
18 | #define OV08X40_REG_MODE_SELECT 0x0100 |
19 | #define OV08X40_MODE_STANDBY 0x00 |
20 | #define OV08X40_MODE_STREAMING 0x01 |
21 | |
22 | #define OV08X40_REG_AO_STANDBY 0x1000 |
23 | #define OV08X40_AO_STREAMING 0x04 |
24 | |
25 | #define OV08X40_REG_MS_SELECT 0x1001 |
26 | #define OV08X40_MS_STANDBY 0x00 |
27 | #define OV08X40_MS_STREAMING 0x04 |
28 | |
29 | #define OV08X40_REG_SOFTWARE_RST 0x0103 |
30 | #define OV08X40_SOFTWARE_RST 0x01 |
31 | |
32 | /* Chip ID */ |
33 | #define OV08X40_REG_CHIP_ID 0x300a |
34 | #define OV08X40_CHIP_ID 0x560858 |
35 | |
36 | /* V_TIMING internal */ |
37 | #define OV08X40_REG_VTS 0x380e |
38 | #define OV08X40_VTS_30FPS 0x09c4 /* the VTS need to be half in normal mode */ |
39 | #define OV08X40_VTS_BIN_30FPS 0x115c |
40 | #define OV08X40_VTS_MAX 0x7fff |
41 | |
42 | /* H TIMING internal */ |
43 | #define OV08X40_REG_HTS 0x380c |
44 | #define OV08X40_HTS_30FPS 0x0280 |
45 | |
46 | /* Exposure control */ |
47 | #define OV08X40_REG_EXPOSURE 0x3500 |
48 | #define OV08X40_EXPOSURE_MAX_MARGIN 8 |
49 | #define OV08X40_EXPOSURE_BIN_MAX_MARGIN 2 |
50 | #define OV08X40_EXPOSURE_MIN 4 |
51 | #define OV08X40_EXPOSURE_STEP 1 |
52 | #define OV08X40_EXPOSURE_DEFAULT 0x40 |
53 | |
54 | /* Short Exposure control */ |
55 | #define OV08X40_REG_SHORT_EXPOSURE 0x3540 |
56 | |
57 | /* Analog gain control */ |
58 | #define OV08X40_REG_ANALOG_GAIN 0x3508 |
59 | #define OV08X40_ANA_GAIN_MIN 0x80 |
60 | #define OV08X40_ANA_GAIN_MAX 0x07c0 |
61 | #define OV08X40_ANA_GAIN_STEP 1 |
62 | #define OV08X40_ANA_GAIN_DEFAULT 0x80 |
63 | |
64 | /* Digital gain control */ |
65 | #define OV08X40_REG_DGTL_GAIN_H 0x350a |
66 | #define OV08X40_REG_DGTL_GAIN_M 0x350b |
67 | #define OV08X40_REG_DGTL_GAIN_L 0x350c |
68 | |
69 | #define OV08X40_DGTL_GAIN_MIN 1024 /* Min = 1 X */ |
70 | #define OV08X40_DGTL_GAIN_MAX (4096 - 1) /* Max = 4 X */ |
71 | #define OV08X40_DGTL_GAIN_DEFAULT 2560 /* Default gain = 2.5 X */ |
72 | #define OV08X40_DGTL_GAIN_STEP 1 /* Each step = 1/1024 */ |
73 | |
74 | #define OV08X40_DGTL_GAIN_L_SHIFT 6 |
75 | #define OV08X40_DGTL_GAIN_L_MASK 0x3 |
76 | #define OV08X40_DGTL_GAIN_M_SHIFT 2 |
77 | #define OV08X40_DGTL_GAIN_M_MASK 0xff |
78 | #define OV08X40_DGTL_GAIN_H_SHIFT 10 |
79 | #define OV08X40_DGTL_GAIN_H_MASK 0x1F |
80 | |
81 | /* Test Pattern Control */ |
82 | #define OV08X40_REG_TEST_PATTERN 0x50C1 |
83 | #define OV08X40_REG_ISP 0x5000 |
84 | #define OV08X40_REG_SHORT_TEST_PATTERN 0x53C1 |
85 | #define OV08X40_TEST_PATTERN_ENABLE BIT(0) |
86 | #define OV08X40_TEST_PATTERN_MASK 0xcf |
87 | #define OV08X40_TEST_PATTERN_BAR_SHIFT 4 |
88 | |
89 | /* Flip Control */ |
90 | #define OV08X40_REG_VFLIP 0x3820 |
91 | #define OV08X40_REG_MIRROR 0x3821 |
92 | |
93 | /* Horizontal Window Offset */ |
94 | #define OV08X40_REG_H_WIN_OFFSET 0x3811 |
95 | |
96 | /* Vertical Window Offset */ |
97 | #define OV08X40_REG_V_WIN_OFFSET 0x3813 |
98 | |
99 | /* Burst Register */ |
100 | #define OV08X40_REG_XTALK_FIRST_A 0x5a80 |
101 | #define OV08X40_REG_XTALK_LAST_A 0x5b9f |
102 | #define OV08X40_REG_XTALK_FIRST_B 0x5bc0 |
103 | #define OV08X40_REG_XTALK_LAST_B 0x5f1f |
104 | |
105 | enum { |
106 | OV08X40_LINK_FREQ_400MHZ_INDEX, |
107 | }; |
108 | |
109 | struct ov08x40_reg { |
110 | u16 address; |
111 | u8 val; |
112 | }; |
113 | |
114 | struct ov08x40_reg_list { |
115 | u32 num_of_regs; |
116 | const struct ov08x40_reg *regs; |
117 | }; |
118 | |
119 | /* Link frequency config */ |
120 | struct ov08x40_link_freq_config { |
121 | /* registers for this link frequency */ |
122 | struct ov08x40_reg_list reg_list; |
123 | }; |
124 | |
125 | /* Mode : resolution and related config&values */ |
126 | struct ov08x40_mode { |
127 | /* Frame width */ |
128 | u32 width; |
129 | /* Frame height */ |
130 | u32 height; |
131 | |
132 | u32 lanes; |
133 | /* V-timing */ |
134 | u32 vts_def; |
135 | u32 vts_min; |
136 | |
137 | /* Line Length Pixels */ |
138 | u32 llp; |
139 | |
140 | /* Index of Link frequency config to be used */ |
141 | u32 link_freq_index; |
142 | /* Default register values */ |
143 | struct ov08x40_reg_list reg_list; |
144 | |
145 | /* Exposure calculation */ |
146 | u16 exposure_margin; |
147 | u16 exposure_shift; |
148 | }; |
149 | |
150 | static const struct ov08x40_reg mipi_data_rate_800mbps[] = { |
151 | {0x0103, 0x01}, |
152 | {0x1000, 0x00}, |
153 | {0x1601, 0xd0}, |
154 | {0x1001, 0x04}, |
155 | {0x5004, 0x53}, |
156 | {0x5110, 0x00}, |
157 | {0x5111, 0x14}, |
158 | {0x5112, 0x01}, |
159 | {0x5113, 0x7b}, |
160 | {0x5114, 0x00}, |
161 | {0x5152, 0xa3}, |
162 | {0x5a52, 0x1f}, |
163 | {0x5a1a, 0x0e}, |
164 | {0x5a1b, 0x10}, |
165 | {0x5a1f, 0x0e}, |
166 | {0x5a27, 0x0e}, |
167 | {0x6002, 0x2e}, |
168 | }; |
169 | |
170 | static const struct ov08x40_reg mode_3856x2416_regs[] = { |
171 | {0x5000, 0x5d}, |
172 | {0x5001, 0x20}, |
173 | {0x5008, 0xb0}, |
174 | {0x50c1, 0x00}, |
175 | {0x53c1, 0x00}, |
176 | {0x5f40, 0x00}, |
177 | {0x5f41, 0x40}, |
178 | {0x0300, 0x3a}, |
179 | {0x0301, 0xc8}, |
180 | {0x0302, 0x31}, |
181 | {0x0303, 0x03}, |
182 | {0x0304, 0x01}, |
183 | {0x0305, 0xa1}, |
184 | {0x0306, 0x04}, |
185 | {0x0307, 0x01}, |
186 | {0x0308, 0x03}, |
187 | {0x0309, 0x03}, |
188 | {0x0310, 0x0a}, |
189 | {0x0311, 0x02}, |
190 | {0x0312, 0x01}, |
191 | {0x0313, 0x08}, |
192 | {0x0314, 0x66}, |
193 | {0x0315, 0x00}, |
194 | {0x0316, 0x34}, |
195 | {0x0320, 0x02}, |
196 | {0x0321, 0x03}, |
197 | {0x0323, 0x05}, |
198 | {0x0324, 0x01}, |
199 | {0x0325, 0xb8}, |
200 | {0x0326, 0x4a}, |
201 | {0x0327, 0x04}, |
202 | {0x0329, 0x00}, |
203 | {0x032a, 0x05}, |
204 | {0x032b, 0x00}, |
205 | {0x032c, 0x00}, |
206 | {0x032d, 0x00}, |
207 | {0x032e, 0x02}, |
208 | {0x032f, 0xa0}, |
209 | {0x0350, 0x00}, |
210 | {0x0360, 0x01}, |
211 | {0x1216, 0x60}, |
212 | {0x1217, 0x5b}, |
213 | {0x1218, 0x00}, |
214 | {0x1220, 0x24}, |
215 | {0x198a, 0x00}, |
216 | {0x198b, 0x01}, |
217 | {0x198e, 0x00}, |
218 | {0x198f, 0x01}, |
219 | {0x3009, 0x04}, |
220 | {0x3012, 0x41}, |
221 | {0x3015, 0x00}, |
222 | {0x3016, 0xb0}, |
223 | {0x3017, 0xf0}, |
224 | {0x3018, 0xf0}, |
225 | {0x3019, 0xd2}, |
226 | {0x301a, 0xb0}, |
227 | {0x301c, 0x81}, |
228 | {0x301d, 0x02}, |
229 | {0x301e, 0x80}, |
230 | {0x3022, 0xf0}, |
231 | {0x3025, 0x89}, |
232 | {0x3030, 0x03}, |
233 | {0x3044, 0xc2}, |
234 | {0x3050, 0x35}, |
235 | {0x3051, 0x60}, |
236 | {0x3052, 0x25}, |
237 | {0x3053, 0x00}, |
238 | {0x3054, 0x00}, |
239 | {0x3055, 0x02}, |
240 | {0x3056, 0x80}, |
241 | {0x3057, 0x80}, |
242 | {0x3058, 0x80}, |
243 | {0x3059, 0x00}, |
244 | {0x3107, 0x86}, |
245 | {0x3400, 0x1c}, |
246 | {0x3401, 0x80}, |
247 | {0x3402, 0x8c}, |
248 | {0x3419, 0x13}, |
249 | {0x341a, 0x89}, |
250 | {0x341b, 0x30}, |
251 | {0x3420, 0x00}, |
252 | {0x3421, 0x00}, |
253 | {0x3422, 0x00}, |
254 | {0x3423, 0x00}, |
255 | {0x3424, 0x00}, |
256 | {0x3425, 0x00}, |
257 | {0x3426, 0x00}, |
258 | {0x3427, 0x00}, |
259 | {0x3428, 0x0f}, |
260 | {0x3429, 0x00}, |
261 | {0x342a, 0x00}, |
262 | {0x342b, 0x00}, |
263 | {0x342c, 0x00}, |
264 | {0x342d, 0x00}, |
265 | {0x342e, 0x00}, |
266 | {0x342f, 0x11}, |
267 | {0x3430, 0x11}, |
268 | {0x3431, 0x10}, |
269 | {0x3432, 0x00}, |
270 | {0x3433, 0x00}, |
271 | {0x3434, 0x00}, |
272 | {0x3435, 0x00}, |
273 | {0x3436, 0x00}, |
274 | {0x3437, 0x00}, |
275 | {0x3442, 0x02}, |
276 | {0x3443, 0x02}, |
277 | {0x3444, 0x07}, |
278 | {0x3450, 0x00}, |
279 | {0x3451, 0x00}, |
280 | {0x3452, 0x18}, |
281 | {0x3453, 0x18}, |
282 | {0x3454, 0x00}, |
283 | {0x3455, 0x80}, |
284 | {0x3456, 0x08}, |
285 | {0x3500, 0x00}, |
286 | {0x3501, 0x02}, |
287 | {0x3502, 0x00}, |
288 | {0x3504, 0x4c}, |
289 | {0x3506, 0x30}, |
290 | {0x3507, 0x00}, |
291 | {0x3508, 0x01}, |
292 | {0x3509, 0x00}, |
293 | {0x350a, 0x01}, |
294 | {0x350b, 0x00}, |
295 | {0x350c, 0x00}, |
296 | {0x3540, 0x00}, |
297 | {0x3541, 0x01}, |
298 | {0x3542, 0x00}, |
299 | {0x3544, 0x4c}, |
300 | {0x3546, 0x30}, |
301 | {0x3547, 0x00}, |
302 | {0x3548, 0x01}, |
303 | {0x3549, 0x00}, |
304 | {0x354a, 0x01}, |
305 | {0x354b, 0x00}, |
306 | {0x354c, 0x00}, |
307 | {0x3688, 0x02}, |
308 | {0x368a, 0x2e}, |
309 | {0x368e, 0x71}, |
310 | {0x3696, 0xd1}, |
311 | {0x3699, 0x00}, |
312 | {0x369a, 0x00}, |
313 | {0x36a4, 0x00}, |
314 | {0x36a6, 0x00}, |
315 | {0x3711, 0x00}, |
316 | {0x3712, 0x51}, |
317 | {0x3713, 0x00}, |
318 | {0x3714, 0x24}, |
319 | {0x3716, 0x00}, |
320 | {0x3718, 0x07}, |
321 | {0x371a, 0x1c}, |
322 | {0x371b, 0x00}, |
323 | {0x3720, 0x08}, |
324 | {0x3725, 0x32}, |
325 | {0x3727, 0x05}, |
326 | {0x3760, 0x02}, |
327 | {0x3761, 0x17}, |
328 | {0x3762, 0x02}, |
329 | {0x3763, 0x02}, |
330 | {0x3764, 0x02}, |
331 | {0x3765, 0x2c}, |
332 | {0x3766, 0x04}, |
333 | {0x3767, 0x2c}, |
334 | {0x3768, 0x02}, |
335 | {0x3769, 0x00}, |
336 | {0x376b, 0x20}, |
337 | {0x376e, 0x03}, |
338 | {0x37b0, 0x00}, |
339 | {0x37b1, 0xab}, |
340 | {0x37b2, 0x01}, |
341 | {0x37b3, 0x82}, |
342 | {0x37b4, 0x00}, |
343 | {0x37b5, 0xe4}, |
344 | {0x37b6, 0x01}, |
345 | {0x37b7, 0xee}, |
346 | {0x3800, 0x00}, |
347 | {0x3801, 0x00}, |
348 | {0x3802, 0x00}, |
349 | {0x3803, 0x00}, |
350 | {0x3804, 0x0f}, |
351 | {0x3805, 0x1f}, |
352 | {0x3806, 0x09}, |
353 | {0x3807, 0x7f}, |
354 | {0x3808, 0x0f}, |
355 | {0x3809, 0x10}, |
356 | {0x380a, 0x09}, |
357 | {0x380b, 0x70}, |
358 | {0x380c, 0x02}, |
359 | {0x380d, 0x80}, |
360 | {0x380e, 0x13}, |
361 | {0x380f, 0x88}, |
362 | {0x3810, 0x00}, |
363 | {0x3811, 0x08}, |
364 | {0x3812, 0x00}, |
365 | {0x3813, 0x07}, |
366 | {0x3814, 0x11}, |
367 | {0x3815, 0x11}, |
368 | {0x3820, 0x00}, |
369 | {0x3821, 0x04}, |
370 | {0x3822, 0x00}, |
371 | {0x3823, 0x04}, |
372 | {0x3828, 0x0f}, |
373 | {0x382a, 0x80}, |
374 | {0x382e, 0x41}, |
375 | {0x3837, 0x08}, |
376 | {0x383a, 0x81}, |
377 | {0x383b, 0x81}, |
378 | {0x383c, 0x11}, |
379 | {0x383d, 0x11}, |
380 | {0x383e, 0x00}, |
381 | {0x383f, 0x38}, |
382 | {0x3840, 0x00}, |
383 | {0x3847, 0x00}, |
384 | {0x384a, 0x00}, |
385 | {0x384c, 0x02}, |
386 | {0x384d, 0x80}, |
387 | {0x3856, 0x50}, |
388 | {0x3857, 0x30}, |
389 | {0x3858, 0x80}, |
390 | {0x3859, 0x40}, |
391 | {0x3860, 0x00}, |
392 | {0x3888, 0x00}, |
393 | {0x3889, 0x00}, |
394 | {0x388a, 0x00}, |
395 | {0x388b, 0x00}, |
396 | {0x388c, 0x00}, |
397 | {0x388d, 0x00}, |
398 | {0x388e, 0x00}, |
399 | {0x388f, 0x00}, |
400 | {0x3894, 0x00}, |
401 | {0x3895, 0x00}, |
402 | {0x3c84, 0x00}, |
403 | {0x3d85, 0x8b}, |
404 | {0x3daa, 0x80}, |
405 | {0x3dab, 0x14}, |
406 | {0x3dac, 0x80}, |
407 | {0x3dad, 0xc8}, |
408 | {0x3dae, 0x81}, |
409 | {0x3daf, 0x7b}, |
410 | {0x3f00, 0x10}, |
411 | {0x3f01, 0x11}, |
412 | {0x3f06, 0x0d}, |
413 | {0x3f07, 0x0b}, |
414 | {0x3f08, 0x0d}, |
415 | {0x3f09, 0x0b}, |
416 | {0x3f0a, 0x01}, |
417 | {0x3f0b, 0x11}, |
418 | {0x3f0c, 0x33}, |
419 | {0x4001, 0x07}, |
420 | {0x4007, 0x20}, |
421 | {0x4008, 0x00}, |
422 | {0x4009, 0x05}, |
423 | {0x400a, 0x00}, |
424 | {0x400b, 0x08}, |
425 | {0x400c, 0x00}, |
426 | {0x400d, 0x08}, |
427 | {0x400e, 0x14}, |
428 | {0x4010, 0xf4}, |
429 | {0x4011, 0x03}, |
430 | {0x4012, 0x55}, |
431 | {0x4015, 0x00}, |
432 | {0x4016, 0x2d}, |
433 | {0x4017, 0x00}, |
434 | {0x4018, 0x0f}, |
435 | {0x401b, 0x08}, |
436 | {0x401c, 0x00}, |
437 | {0x401d, 0x10}, |
438 | {0x401e, 0x02}, |
439 | {0x401f, 0x00}, |
440 | {0x4050, 0x06}, |
441 | {0x4051, 0xff}, |
442 | {0x4052, 0xff}, |
443 | {0x4053, 0xff}, |
444 | {0x4054, 0xff}, |
445 | {0x4055, 0xff}, |
446 | {0x4056, 0xff}, |
447 | {0x4057, 0x7f}, |
448 | {0x4058, 0x00}, |
449 | {0x4059, 0x00}, |
450 | {0x405a, 0x00}, |
451 | {0x405b, 0x00}, |
452 | {0x405c, 0x07}, |
453 | {0x405d, 0xff}, |
454 | {0x405e, 0x07}, |
455 | {0x405f, 0xff}, |
456 | {0x4080, 0x78}, |
457 | {0x4081, 0x78}, |
458 | {0x4082, 0x78}, |
459 | {0x4083, 0x78}, |
460 | {0x4019, 0x00}, |
461 | {0x401a, 0x40}, |
462 | {0x4020, 0x04}, |
463 | {0x4021, 0x00}, |
464 | {0x4022, 0x04}, |
465 | {0x4023, 0x00}, |
466 | {0x4024, 0x04}, |
467 | {0x4025, 0x00}, |
468 | {0x4026, 0x04}, |
469 | {0x4027, 0x00}, |
470 | {0x4030, 0x00}, |
471 | {0x4031, 0x00}, |
472 | {0x4032, 0x00}, |
473 | {0x4033, 0x00}, |
474 | {0x4034, 0x00}, |
475 | {0x4035, 0x00}, |
476 | {0x4036, 0x00}, |
477 | {0x4037, 0x00}, |
478 | {0x4040, 0x00}, |
479 | {0x4041, 0x80}, |
480 | {0x4042, 0x00}, |
481 | {0x4043, 0x80}, |
482 | {0x4044, 0x00}, |
483 | {0x4045, 0x80}, |
484 | {0x4046, 0x00}, |
485 | {0x4047, 0x80}, |
486 | {0x4060, 0x00}, |
487 | {0x4061, 0x00}, |
488 | {0x4062, 0x00}, |
489 | {0x4063, 0x00}, |
490 | {0x4064, 0x00}, |
491 | {0x4065, 0x00}, |
492 | {0x4066, 0x00}, |
493 | {0x4067, 0x00}, |
494 | {0x4068, 0x00}, |
495 | {0x4069, 0x00}, |
496 | {0x406a, 0x00}, |
497 | {0x406b, 0x00}, |
498 | {0x406c, 0x00}, |
499 | {0x406d, 0x00}, |
500 | {0x406e, 0x00}, |
501 | {0x406f, 0x00}, |
502 | {0x4070, 0x00}, |
503 | {0x4071, 0x00}, |
504 | {0x4072, 0x00}, |
505 | {0x4073, 0x00}, |
506 | {0x4074, 0x00}, |
507 | {0x4075, 0x00}, |
508 | {0x4076, 0x00}, |
509 | {0x4077, 0x00}, |
510 | {0x4078, 0x00}, |
511 | {0x4079, 0x00}, |
512 | {0x407a, 0x00}, |
513 | {0x407b, 0x00}, |
514 | {0x407c, 0x00}, |
515 | {0x407d, 0x00}, |
516 | {0x407e, 0x00}, |
517 | {0x407f, 0x00}, |
518 | {0x40e0, 0x00}, |
519 | {0x40e1, 0x00}, |
520 | {0x40e2, 0x00}, |
521 | {0x40e3, 0x00}, |
522 | {0x40e4, 0x00}, |
523 | {0x40e5, 0x00}, |
524 | {0x40e6, 0x00}, |
525 | {0x40e7, 0x00}, |
526 | {0x40e8, 0x00}, |
527 | {0x40e9, 0x80}, |
528 | {0x40ea, 0x00}, |
529 | {0x40eb, 0x80}, |
530 | {0x40ec, 0x00}, |
531 | {0x40ed, 0x80}, |
532 | {0x40ee, 0x00}, |
533 | {0x40ef, 0x80}, |
534 | {0x40f0, 0x02}, |
535 | {0x40f1, 0x04}, |
536 | {0x4300, 0x00}, |
537 | {0x4301, 0x00}, |
538 | {0x4302, 0x00}, |
539 | {0x4303, 0x00}, |
540 | {0x4304, 0x00}, |
541 | {0x4305, 0x00}, |
542 | {0x4306, 0x00}, |
543 | {0x4307, 0x00}, |
544 | {0x4308, 0x00}, |
545 | {0x4309, 0x00}, |
546 | {0x430a, 0x00}, |
547 | {0x430b, 0xff}, |
548 | {0x430c, 0xff}, |
549 | {0x430d, 0x00}, |
550 | {0x430e, 0x00}, |
551 | {0x4315, 0x00}, |
552 | {0x4316, 0x00}, |
553 | {0x4317, 0x00}, |
554 | {0x4318, 0x00}, |
555 | {0x4319, 0x00}, |
556 | {0x431a, 0x00}, |
557 | {0x431b, 0x00}, |
558 | {0x431c, 0x00}, |
559 | {0x4500, 0x07}, |
560 | {0x4501, 0x00}, |
561 | {0x4502, 0x00}, |
562 | {0x4503, 0x0f}, |
563 | {0x4504, 0x80}, |
564 | {0x4506, 0x01}, |
565 | {0x4509, 0x05}, |
566 | {0x450c, 0x00}, |
567 | {0x450d, 0x20}, |
568 | {0x450e, 0x00}, |
569 | {0x450f, 0x00}, |
570 | {0x4510, 0x00}, |
571 | {0x4523, 0x00}, |
572 | {0x4526, 0x00}, |
573 | {0x4542, 0x00}, |
574 | {0x4543, 0x00}, |
575 | {0x4544, 0x00}, |
576 | {0x4545, 0x00}, |
577 | {0x4546, 0x00}, |
578 | {0x4547, 0x10}, |
579 | {0x4602, 0x00}, |
580 | {0x4603, 0x15}, |
581 | {0x460b, 0x07}, |
582 | {0x4680, 0x11}, |
583 | {0x4686, 0x00}, |
584 | {0x4687, 0x00}, |
585 | {0x4700, 0x00}, |
586 | {0x4800, 0x64}, |
587 | {0x4806, 0x40}, |
588 | {0x480b, 0x10}, |
589 | {0x480c, 0x80}, |
590 | {0x480f, 0x32}, |
591 | {0x4813, 0xe4}, |
592 | {0x4837, 0x14}, |
593 | {0x4850, 0x42}, |
594 | {0x4884, 0x04}, |
595 | {0x4c00, 0xf8}, |
596 | {0x4c01, 0x44}, |
597 | {0x4c03, 0x00}, |
598 | {0x4d00, 0x00}, |
599 | {0x4d01, 0x16}, |
600 | {0x4d04, 0x10}, |
601 | {0x4d05, 0x00}, |
602 | {0x4d06, 0x0c}, |
603 | {0x4d07, 0x00}, |
604 | {0x3d84, 0x04}, |
605 | {0x3680, 0xa4}, |
606 | {0x3682, 0x80}, |
607 | {0x3601, 0x40}, |
608 | {0x3602, 0x90}, |
609 | {0x3608, 0x0a}, |
610 | {0x3938, 0x09}, |
611 | {0x3a74, 0x84}, |
612 | {0x3a99, 0x84}, |
613 | {0x3ab9, 0xa6}, |
614 | {0x3aba, 0xba}, |
615 | {0x3b12, 0x84}, |
616 | {0x3b14, 0xbb}, |
617 | {0x3b15, 0xbf}, |
618 | {0x3a29, 0x26}, |
619 | {0x3a1f, 0x8a}, |
620 | {0x3a22, 0x91}, |
621 | {0x3a25, 0x96}, |
622 | {0x3a28, 0xb4}, |
623 | {0x3a2b, 0xba}, |
624 | {0x3a2e, 0xbf}, |
625 | {0x3a31, 0xc1}, |
626 | {0x3a20, 0x00}, |
627 | {0x3939, 0x9d}, |
628 | {0x3902, 0x0e}, |
629 | {0x3903, 0x0e}, |
630 | {0x3904, 0x0e}, |
631 | {0x3905, 0x0e}, |
632 | {0x3906, 0x07}, |
633 | {0x3907, 0x0d}, |
634 | {0x3908, 0x11}, |
635 | {0x3909, 0x12}, |
636 | {0x360f, 0x99}, |
637 | {0x390c, 0x33}, |
638 | {0x390d, 0x66}, |
639 | {0x390e, 0xaa}, |
640 | {0x3911, 0x90}, |
641 | {0x3913, 0x90}, |
642 | {0x3915, 0x90}, |
643 | {0x3917, 0x90}, |
644 | {0x3b3f, 0x9d}, |
645 | {0x3b45, 0x9d}, |
646 | {0x3b1b, 0xc9}, |
647 | {0x3b21, 0xc9}, |
648 | {0x3440, 0xa4}, |
649 | {0x3a23, 0x15}, |
650 | {0x3a26, 0x1d}, |
651 | {0x3a2c, 0x4a}, |
652 | {0x3a2f, 0x18}, |
653 | {0x3a32, 0x55}, |
654 | {0x3b0a, 0x01}, |
655 | {0x3b0b, 0x00}, |
656 | {0x3b0e, 0x01}, |
657 | {0x3b0f, 0x00}, |
658 | {0x392c, 0x02}, |
659 | {0x392d, 0x02}, |
660 | {0x392e, 0x04}, |
661 | {0x392f, 0x03}, |
662 | {0x3930, 0x08}, |
663 | {0x3931, 0x07}, |
664 | {0x3932, 0x10}, |
665 | {0x3933, 0x0c}, |
666 | {0x3609, 0x08}, |
667 | {0x3921, 0x0f}, |
668 | {0x3928, 0x15}, |
669 | {0x3929, 0x2a}, |
670 | {0x392a, 0x54}, |
671 | {0x392b, 0xa8}, |
672 | {0x3426, 0x10}, |
673 | {0x3407, 0x01}, |
674 | {0x3404, 0x01}, |
675 | {0x3500, 0x00}, |
676 | {0x3501, 0x10}, |
677 | {0x3502, 0x10}, |
678 | {0x3508, 0x0f}, |
679 | {0x3509, 0x80}, |
680 | }; |
681 | |
682 | static const struct ov08x40_reg mode_1928x1208_regs[] = { |
683 | {0x5000, 0x55}, |
684 | {0x5001, 0x00}, |
685 | {0x5008, 0xb0}, |
686 | {0x50c1, 0x00}, |
687 | {0x53c1, 0x00}, |
688 | {0x5f40, 0x00}, |
689 | {0x5f41, 0x40}, |
690 | {0x0300, 0x3a}, |
691 | {0x0301, 0xc8}, |
692 | {0x0302, 0x31}, |
693 | {0x0303, 0x03}, |
694 | {0x0304, 0x01}, |
695 | {0x0305, 0xa1}, |
696 | {0x0306, 0x04}, |
697 | {0x0307, 0x01}, |
698 | {0x0308, 0x03}, |
699 | {0x0309, 0x03}, |
700 | {0x0310, 0x0a}, |
701 | {0x0311, 0x02}, |
702 | {0x0312, 0x01}, |
703 | {0x0313, 0x08}, |
704 | {0x0314, 0x66}, |
705 | {0x0315, 0x00}, |
706 | {0x0316, 0x34}, |
707 | {0x0320, 0x02}, |
708 | {0x0321, 0x03}, |
709 | {0x0323, 0x05}, |
710 | {0x0324, 0x01}, |
711 | {0x0325, 0xb8}, |
712 | {0x0326, 0x4a}, |
713 | {0x0327, 0x04}, |
714 | {0x0329, 0x00}, |
715 | {0x032a, 0x05}, |
716 | {0x032b, 0x00}, |
717 | {0x032c, 0x00}, |
718 | {0x032d, 0x00}, |
719 | {0x032e, 0x02}, |
720 | {0x032f, 0xa0}, |
721 | {0x0350, 0x00}, |
722 | {0x0360, 0x01}, |
723 | {0x1216, 0x60}, |
724 | {0x1217, 0x5b}, |
725 | {0x1218, 0x00}, |
726 | {0x1220, 0x24}, |
727 | {0x198a, 0x00}, |
728 | {0x198b, 0x01}, |
729 | {0x198e, 0x00}, |
730 | {0x198f, 0x01}, |
731 | {0x3009, 0x04}, |
732 | {0x3012, 0x41}, |
733 | {0x3015, 0x00}, |
734 | {0x3016, 0xb0}, |
735 | {0x3017, 0xf0}, |
736 | {0x3018, 0xf0}, |
737 | {0x3019, 0xd2}, |
738 | {0x301a, 0xb0}, |
739 | {0x301c, 0x81}, |
740 | {0x301d, 0x02}, |
741 | {0x301e, 0x80}, |
742 | {0x3022, 0xf0}, |
743 | {0x3025, 0x89}, |
744 | {0x3030, 0x03}, |
745 | {0x3044, 0xc2}, |
746 | {0x3050, 0x35}, |
747 | {0x3051, 0x60}, |
748 | {0x3052, 0x25}, |
749 | {0x3053, 0x00}, |
750 | {0x3054, 0x00}, |
751 | {0x3055, 0x02}, |
752 | {0x3056, 0x80}, |
753 | {0x3057, 0x80}, |
754 | {0x3058, 0x80}, |
755 | {0x3059, 0x00}, |
756 | {0x3107, 0x86}, |
757 | {0x3400, 0x1c}, |
758 | {0x3401, 0x80}, |
759 | {0x3402, 0x8c}, |
760 | {0x3419, 0x08}, |
761 | {0x341a, 0xaf}, |
762 | {0x341b, 0x30}, |
763 | {0x3420, 0x00}, |
764 | {0x3421, 0x00}, |
765 | {0x3422, 0x00}, |
766 | {0x3423, 0x00}, |
767 | {0x3424, 0x00}, |
768 | {0x3425, 0x00}, |
769 | {0x3426, 0x00}, |
770 | {0x3427, 0x00}, |
771 | {0x3428, 0x0f}, |
772 | {0x3429, 0x00}, |
773 | {0x342a, 0x00}, |
774 | {0x342b, 0x00}, |
775 | {0x342c, 0x00}, |
776 | {0x342d, 0x00}, |
777 | {0x342e, 0x00}, |
778 | {0x342f, 0x11}, |
779 | {0x3430, 0x11}, |
780 | {0x3431, 0x10}, |
781 | {0x3432, 0x00}, |
782 | {0x3433, 0x00}, |
783 | {0x3434, 0x00}, |
784 | {0x3435, 0x00}, |
785 | {0x3436, 0x00}, |
786 | {0x3437, 0x00}, |
787 | {0x3442, 0x02}, |
788 | {0x3443, 0x02}, |
789 | {0x3444, 0x07}, |
790 | {0x3450, 0x00}, |
791 | {0x3451, 0x00}, |
792 | {0x3452, 0x18}, |
793 | {0x3453, 0x18}, |
794 | {0x3454, 0x00}, |
795 | {0x3455, 0x80}, |
796 | {0x3456, 0x08}, |
797 | {0x3500, 0x00}, |
798 | {0x3501, 0x02}, |
799 | {0x3502, 0x00}, |
800 | {0x3504, 0x4c}, |
801 | {0x3506, 0x30}, |
802 | {0x3507, 0x00}, |
803 | {0x3508, 0x01}, |
804 | {0x3509, 0x00}, |
805 | {0x350a, 0x01}, |
806 | {0x350b, 0x00}, |
807 | {0x350c, 0x00}, |
808 | {0x3540, 0x00}, |
809 | {0x3541, 0x01}, |
810 | {0x3542, 0x00}, |
811 | {0x3544, 0x4c}, |
812 | {0x3546, 0x30}, |
813 | {0x3547, 0x00}, |
814 | {0x3548, 0x01}, |
815 | {0x3549, 0x00}, |
816 | {0x354a, 0x01}, |
817 | {0x354b, 0x00}, |
818 | {0x354c, 0x00}, |
819 | {0x3688, 0x02}, |
820 | {0x368a, 0x2e}, |
821 | {0x368e, 0x71}, |
822 | {0x3696, 0xd1}, |
823 | {0x3699, 0x00}, |
824 | {0x369a, 0x00}, |
825 | {0x36a4, 0x00}, |
826 | {0x36a6, 0x00}, |
827 | {0x3711, 0x00}, |
828 | {0x3712, 0x50}, |
829 | {0x3713, 0x00}, |
830 | {0x3714, 0x21}, |
831 | {0x3716, 0x00}, |
832 | {0x3718, 0x07}, |
833 | {0x371a, 0x1c}, |
834 | {0x371b, 0x00}, |
835 | {0x3720, 0x08}, |
836 | {0x3725, 0x32}, |
837 | {0x3727, 0x05}, |
838 | {0x3760, 0x02}, |
839 | {0x3761, 0x28}, |
840 | {0x3762, 0x02}, |
841 | {0x3763, 0x02}, |
842 | {0x3764, 0x02}, |
843 | {0x3765, 0x2c}, |
844 | {0x3766, 0x04}, |
845 | {0x3767, 0x2c}, |
846 | {0x3768, 0x02}, |
847 | {0x3769, 0x00}, |
848 | {0x376b, 0x20}, |
849 | {0x376e, 0x07}, |
850 | {0x37b0, 0x01}, |
851 | {0x37b1, 0x0f}, |
852 | {0x37b2, 0x01}, |
853 | {0x37b3, 0xd6}, |
854 | {0x37b4, 0x01}, |
855 | {0x37b5, 0x48}, |
856 | {0x37b6, 0x02}, |
857 | {0x37b7, 0x40}, |
858 | {0x3800, 0x00}, |
859 | {0x3801, 0x00}, |
860 | {0x3802, 0x00}, |
861 | {0x3803, 0x00}, |
862 | {0x3804, 0x0f}, |
863 | {0x3805, 0x1f}, |
864 | {0x3806, 0x09}, |
865 | {0x3807, 0x7f}, |
866 | {0x3808, 0x07}, |
867 | {0x3809, 0x88}, |
868 | {0x380a, 0x04}, |
869 | {0x380b, 0xb8}, |
870 | {0x380c, 0x02}, |
871 | {0x380d, 0xd0}, |
872 | {0x380e, 0x11}, |
873 | {0x380f, 0x5c}, |
874 | {0x3810, 0x00}, |
875 | {0x3811, 0x04}, |
876 | {0x3812, 0x00}, |
877 | {0x3813, 0x03}, |
878 | {0x3814, 0x11}, |
879 | {0x3815, 0x11}, |
880 | {0x3820, 0x02}, |
881 | {0x3821, 0x14}, |
882 | {0x3822, 0x00}, |
883 | {0x3823, 0x04}, |
884 | {0x3828, 0x0f}, |
885 | {0x382a, 0x80}, |
886 | {0x382e, 0x41}, |
887 | {0x3837, 0x08}, |
888 | {0x383a, 0x81}, |
889 | {0x383b, 0x81}, |
890 | {0x383c, 0x11}, |
891 | {0x383d, 0x11}, |
892 | {0x383e, 0x00}, |
893 | {0x383f, 0x38}, |
894 | {0x3840, 0x00}, |
895 | {0x3847, 0x00}, |
896 | {0x384a, 0x00}, |
897 | {0x384c, 0x02}, |
898 | {0x384d, 0xd0}, |
899 | {0x3856, 0x50}, |
900 | {0x3857, 0x30}, |
901 | {0x3858, 0x80}, |
902 | {0x3859, 0x40}, |
903 | {0x3860, 0x00}, |
904 | {0x3888, 0x00}, |
905 | {0x3889, 0x00}, |
906 | {0x388a, 0x00}, |
907 | {0x388b, 0x00}, |
908 | {0x388c, 0x00}, |
909 | {0x388d, 0x00}, |
910 | {0x388e, 0x00}, |
911 | {0x388f, 0x00}, |
912 | {0x3894, 0x00}, |
913 | {0x3895, 0x00}, |
914 | {0x3c84, 0x00}, |
915 | {0x3d85, 0x8b}, |
916 | {0x3daa, 0x80}, |
917 | {0x3dab, 0x14}, |
918 | {0x3dac, 0x80}, |
919 | {0x3dad, 0xc8}, |
920 | {0x3dae, 0x81}, |
921 | {0x3daf, 0x7b}, |
922 | {0x3f00, 0x10}, |
923 | {0x3f01, 0x11}, |
924 | {0x3f06, 0x0d}, |
925 | {0x3f07, 0x0b}, |
926 | {0x3f08, 0x0d}, |
927 | {0x3f09, 0x0b}, |
928 | {0x3f0a, 0x01}, |
929 | {0x3f0b, 0x11}, |
930 | {0x3f0c, 0x33}, |
931 | {0x4001, 0x07}, |
932 | {0x4007, 0x20}, |
933 | {0x4008, 0x00}, |
934 | {0x4009, 0x05}, |
935 | {0x400a, 0x00}, |
936 | {0x400b, 0x04}, |
937 | {0x400c, 0x00}, |
938 | {0x400d, 0x04}, |
939 | {0x400e, 0x14}, |
940 | {0x4010, 0xf4}, |
941 | {0x4011, 0x03}, |
942 | {0x4012, 0x55}, |
943 | {0x4015, 0x00}, |
944 | {0x4016, 0x27}, |
945 | {0x4017, 0x00}, |
946 | {0x4018, 0x0f}, |
947 | {0x401b, 0x08}, |
948 | {0x401c, 0x00}, |
949 | {0x401d, 0x10}, |
950 | {0x401e, 0x02}, |
951 | {0x401f, 0x00}, |
952 | {0x4050, 0x06}, |
953 | {0x4051, 0xff}, |
954 | {0x4052, 0xff}, |
955 | {0x4053, 0xff}, |
956 | {0x4054, 0xff}, |
957 | {0x4055, 0xff}, |
958 | {0x4056, 0xff}, |
959 | {0x4057, 0x7f}, |
960 | {0x4058, 0x00}, |
961 | {0x4059, 0x00}, |
962 | {0x405a, 0x00}, |
963 | {0x405b, 0x00}, |
964 | {0x405c, 0x07}, |
965 | {0x405d, 0xff}, |
966 | {0x405e, 0x07}, |
967 | {0x405f, 0xff}, |
968 | {0x4080, 0x78}, |
969 | {0x4081, 0x78}, |
970 | {0x4082, 0x78}, |
971 | {0x4083, 0x78}, |
972 | {0x4019, 0x00}, |
973 | {0x401a, 0x40}, |
974 | {0x4020, 0x04}, |
975 | {0x4021, 0x00}, |
976 | {0x4022, 0x04}, |
977 | {0x4023, 0x00}, |
978 | {0x4024, 0x04}, |
979 | {0x4025, 0x00}, |
980 | {0x4026, 0x04}, |
981 | {0x4027, 0x00}, |
982 | {0x4030, 0x00}, |
983 | {0x4031, 0x00}, |
984 | {0x4032, 0x00}, |
985 | {0x4033, 0x00}, |
986 | {0x4034, 0x00}, |
987 | {0x4035, 0x00}, |
988 | {0x4036, 0x00}, |
989 | {0x4037, 0x00}, |
990 | {0x4040, 0x00}, |
991 | {0x4041, 0x80}, |
992 | {0x4042, 0x00}, |
993 | {0x4043, 0x80}, |
994 | {0x4044, 0x00}, |
995 | {0x4045, 0x80}, |
996 | {0x4046, 0x00}, |
997 | {0x4047, 0x80}, |
998 | {0x4060, 0x00}, |
999 | {0x4061, 0x00}, |
1000 | {0x4062, 0x00}, |
1001 | {0x4063, 0x00}, |
1002 | {0x4064, 0x00}, |
1003 | {0x4065, 0x00}, |
1004 | {0x4066, 0x00}, |
1005 | {0x4067, 0x00}, |
1006 | {0x4068, 0x00}, |
1007 | {0x4069, 0x00}, |
1008 | {0x406a, 0x00}, |
1009 | {0x406b, 0x00}, |
1010 | {0x406c, 0x00}, |
1011 | {0x406d, 0x00}, |
1012 | {0x406e, 0x00}, |
1013 | {0x406f, 0x00}, |
1014 | {0x4070, 0x00}, |
1015 | {0x4071, 0x00}, |
1016 | {0x4072, 0x00}, |
1017 | {0x4073, 0x00}, |
1018 | {0x4074, 0x00}, |
1019 | {0x4075, 0x00}, |
1020 | {0x4076, 0x00}, |
1021 | {0x4077, 0x00}, |
1022 | {0x4078, 0x00}, |
1023 | {0x4079, 0x00}, |
1024 | {0x407a, 0x00}, |
1025 | {0x407b, 0x00}, |
1026 | {0x407c, 0x00}, |
1027 | {0x407d, 0x00}, |
1028 | {0x407e, 0x00}, |
1029 | {0x407f, 0x00}, |
1030 | {0x40e0, 0x00}, |
1031 | {0x40e1, 0x00}, |
1032 | {0x40e2, 0x00}, |
1033 | {0x40e3, 0x00}, |
1034 | {0x40e4, 0x00}, |
1035 | {0x40e5, 0x00}, |
1036 | {0x40e6, 0x00}, |
1037 | {0x40e7, 0x00}, |
1038 | {0x40e8, 0x00}, |
1039 | {0x40e9, 0x80}, |
1040 | {0x40ea, 0x00}, |
1041 | {0x40eb, 0x80}, |
1042 | {0x40ec, 0x00}, |
1043 | {0x40ed, 0x80}, |
1044 | {0x40ee, 0x00}, |
1045 | {0x40ef, 0x80}, |
1046 | {0x40f0, 0x02}, |
1047 | {0x40f1, 0x04}, |
1048 | {0x4300, 0x00}, |
1049 | {0x4301, 0x00}, |
1050 | {0x4302, 0x00}, |
1051 | {0x4303, 0x00}, |
1052 | {0x4304, 0x00}, |
1053 | {0x4305, 0x00}, |
1054 | {0x4306, 0x00}, |
1055 | {0x4307, 0x00}, |
1056 | {0x4308, 0x00}, |
1057 | {0x4309, 0x00}, |
1058 | {0x430a, 0x00}, |
1059 | {0x430b, 0xff}, |
1060 | {0x430c, 0xff}, |
1061 | {0x430d, 0x00}, |
1062 | {0x430e, 0x00}, |
1063 | {0x4315, 0x00}, |
1064 | {0x4316, 0x00}, |
1065 | {0x4317, 0x00}, |
1066 | {0x4318, 0x00}, |
1067 | {0x4319, 0x00}, |
1068 | {0x431a, 0x00}, |
1069 | {0x431b, 0x00}, |
1070 | {0x431c, 0x00}, |
1071 | {0x4500, 0x07}, |
1072 | {0x4501, 0x10}, |
1073 | {0x4502, 0x00}, |
1074 | {0x4503, 0x0f}, |
1075 | {0x4504, 0x80}, |
1076 | {0x4506, 0x01}, |
1077 | {0x4509, 0x05}, |
1078 | {0x450c, 0x00}, |
1079 | {0x450d, 0x20}, |
1080 | {0x450e, 0x00}, |
1081 | {0x450f, 0x00}, |
1082 | {0x4510, 0x00}, |
1083 | {0x4523, 0x00}, |
1084 | {0x4526, 0x00}, |
1085 | {0x4542, 0x00}, |
1086 | {0x4543, 0x00}, |
1087 | {0x4544, 0x00}, |
1088 | {0x4545, 0x00}, |
1089 | {0x4546, 0x00}, |
1090 | {0x4547, 0x10}, |
1091 | {0x4602, 0x00}, |
1092 | {0x4603, 0x15}, |
1093 | {0x460b, 0x07}, |
1094 | {0x4680, 0x11}, |
1095 | {0x4686, 0x00}, |
1096 | {0x4687, 0x00}, |
1097 | {0x4700, 0x00}, |
1098 | {0x4800, 0x64}, |
1099 | {0x4806, 0x40}, |
1100 | {0x480b, 0x10}, |
1101 | {0x480c, 0x80}, |
1102 | {0x480f, 0x32}, |
1103 | {0x4813, 0xe4}, |
1104 | {0x4837, 0x14}, |
1105 | {0x4850, 0x42}, |
1106 | {0x4884, 0x04}, |
1107 | {0x4c00, 0xf8}, |
1108 | {0x4c01, 0x44}, |
1109 | {0x4c03, 0x00}, |
1110 | {0x4d00, 0x00}, |
1111 | {0x4d01, 0x16}, |
1112 | {0x4d04, 0x10}, |
1113 | {0x4d05, 0x00}, |
1114 | {0x4d06, 0x0c}, |
1115 | {0x4d07, 0x00}, |
1116 | {0x3d84, 0x04}, |
1117 | {0x3680, 0xa4}, |
1118 | {0x3682, 0x80}, |
1119 | {0x3601, 0x40}, |
1120 | {0x3602, 0x90}, |
1121 | {0x3608, 0x0a}, |
1122 | {0x3938, 0x09}, |
1123 | {0x3a74, 0x84}, |
1124 | {0x3a99, 0x84}, |
1125 | {0x3ab9, 0xa6}, |
1126 | {0x3aba, 0xba}, |
1127 | {0x3b12, 0x84}, |
1128 | {0x3b14, 0xbb}, |
1129 | {0x3b15, 0xbf}, |
1130 | {0x3a29, 0x26}, |
1131 | {0x3a1f, 0x8a}, |
1132 | {0x3a22, 0x91}, |
1133 | {0x3a25, 0x96}, |
1134 | {0x3a28, 0xb4}, |
1135 | {0x3a2b, 0xba}, |
1136 | {0x3a2e, 0xbf}, |
1137 | {0x3a31, 0xc1}, |
1138 | {0x3a20, 0x05}, |
1139 | {0x3939, 0x6b}, |
1140 | {0x3902, 0x10}, |
1141 | {0x3903, 0x10}, |
1142 | {0x3904, 0x10}, |
1143 | {0x3905, 0x10}, |
1144 | {0x3906, 0x01}, |
1145 | {0x3907, 0x0b}, |
1146 | {0x3908, 0x10}, |
1147 | {0x3909, 0x13}, |
1148 | {0x360f, 0x99}, |
1149 | {0x390b, 0x11}, |
1150 | {0x390c, 0x21}, |
1151 | {0x390d, 0x32}, |
1152 | {0x390e, 0x76}, |
1153 | {0x3911, 0x90}, |
1154 | {0x3913, 0x90}, |
1155 | {0x3b3f, 0x9d}, |
1156 | {0x3b45, 0x9d}, |
1157 | {0x3b1b, 0xc9}, |
1158 | {0x3b21, 0xc9}, |
1159 | {0x3a1a, 0x1c}, |
1160 | {0x3a23, 0x15}, |
1161 | {0x3a26, 0x17}, |
1162 | {0x3a2c, 0x50}, |
1163 | {0x3a2f, 0x18}, |
1164 | {0x3a32, 0x4f}, |
1165 | {0x3ace, 0x01}, |
1166 | {0x3ad2, 0x01}, |
1167 | {0x3ad6, 0x01}, |
1168 | {0x3ada, 0x01}, |
1169 | {0x3ade, 0x01}, |
1170 | {0x3ae2, 0x01}, |
1171 | {0x3aee, 0x01}, |
1172 | {0x3af2, 0x01}, |
1173 | {0x3af6, 0x01}, |
1174 | {0x3afa, 0x01}, |
1175 | {0x3afe, 0x01}, |
1176 | {0x3b02, 0x01}, |
1177 | {0x3b06, 0x01}, |
1178 | {0x3b0a, 0x01}, |
1179 | {0x3b0b, 0x00}, |
1180 | {0x3b0e, 0x01}, |
1181 | {0x3b0f, 0x00}, |
1182 | {0x392c, 0x02}, |
1183 | {0x392d, 0x01}, |
1184 | {0x392e, 0x04}, |
1185 | {0x392f, 0x03}, |
1186 | {0x3930, 0x09}, |
1187 | {0x3931, 0x07}, |
1188 | {0x3932, 0x10}, |
1189 | {0x3933, 0x0d}, |
1190 | {0x3609, 0x08}, |
1191 | {0x3921, 0x0f}, |
1192 | {0x3928, 0x15}, |
1193 | {0x3929, 0x2a}, |
1194 | {0x392a, 0x52}, |
1195 | {0x392b, 0xa3}, |
1196 | {0x340b, 0x1b}, |
1197 | {0x3426, 0x10}, |
1198 | {0x3407, 0x01}, |
1199 | {0x3404, 0x01}, |
1200 | {0x3500, 0x00}, |
1201 | {0x3501, 0x08}, |
1202 | {0x3502, 0x10}, |
1203 | {0x3508, 0x04}, |
1204 | {0x3509, 0x00}, |
1205 | }; |
1206 | |
1207 | static const char * const [] = { |
1208 | "Disabled" , |
1209 | "Vertical Color Bar Type 1" , |
1210 | "Vertical Color Bar Type 2" , |
1211 | "Vertical Color Bar Type 3" , |
1212 | "Vertical Color Bar Type 4" |
1213 | }; |
1214 | |
1215 | /* Configurations for supported link frequencies */ |
1216 | #define OV08X40_LINK_FREQ_400MHZ 400000000ULL |
1217 | #define OV08X40_SCLK_96MHZ 96000000ULL |
1218 | #define OV08X40_EXT_CLK 19200000 |
1219 | #define OV08X40_DATA_LANES 4 |
1220 | |
1221 | /* |
1222 | * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample |
1223 | * data rate => double data rate; number of lanes => 4; bits per pixel => 10 |
1224 | */ |
1225 | static u64 link_freq_to_pixel_rate(u64 f) |
1226 | { |
1227 | f *= 2 * OV08X40_DATA_LANES; |
1228 | do_div(f, 10); |
1229 | |
1230 | return f; |
1231 | } |
1232 | |
1233 | /* Menu items for LINK_FREQ V4L2 control */ |
1234 | static const s64 [] = { |
1235 | OV08X40_LINK_FREQ_400MHZ, |
1236 | }; |
1237 | |
1238 | /* Link frequency configs */ |
1239 | static const struct ov08x40_link_freq_config link_freq_configs[] = { |
1240 | [OV08X40_LINK_FREQ_400MHZ_INDEX] = { |
1241 | .reg_list = { |
1242 | .num_of_regs = ARRAY_SIZE(mipi_data_rate_800mbps), |
1243 | .regs = mipi_data_rate_800mbps, |
1244 | } |
1245 | }, |
1246 | }; |
1247 | |
1248 | /* Mode configs */ |
1249 | static const struct ov08x40_mode supported_modes[] = { |
1250 | { |
1251 | .width = 3856, |
1252 | .height = 2416, |
1253 | .vts_def = OV08X40_VTS_30FPS, |
1254 | .vts_min = OV08X40_VTS_30FPS, |
1255 | .llp = 0x10aa, /* in normal mode, tline time = 2 * HTS / SCLK */ |
1256 | .lanes = 4, |
1257 | .reg_list = { |
1258 | .num_of_regs = ARRAY_SIZE(mode_3856x2416_regs), |
1259 | .regs = mode_3856x2416_regs, |
1260 | }, |
1261 | .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, |
1262 | .exposure_shift = 1, |
1263 | .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, |
1264 | }, |
1265 | { |
1266 | .width = 1928, |
1267 | .height = 1208, |
1268 | .vts_def = OV08X40_VTS_BIN_30FPS, |
1269 | .vts_min = OV08X40_VTS_BIN_30FPS, |
1270 | .llp = 0x960, |
1271 | .lanes = 4, |
1272 | .reg_list = { |
1273 | .num_of_regs = ARRAY_SIZE(mode_1928x1208_regs), |
1274 | .regs = mode_1928x1208_regs, |
1275 | }, |
1276 | .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, |
1277 | .exposure_shift = 0, |
1278 | .exposure_margin = OV08X40_EXPOSURE_BIN_MAX_MARGIN, |
1279 | }, |
1280 | }; |
1281 | |
1282 | struct ov08x40 { |
1283 | struct v4l2_subdev sd; |
1284 | struct media_pad pad; |
1285 | |
1286 | struct v4l2_ctrl_handler ctrl_handler; |
1287 | /* V4L2 Controls */ |
1288 | struct v4l2_ctrl *link_freq; |
1289 | struct v4l2_ctrl *pixel_rate; |
1290 | struct v4l2_ctrl *vblank; |
1291 | struct v4l2_ctrl *hblank; |
1292 | struct v4l2_ctrl *exposure; |
1293 | |
1294 | /* Current mode */ |
1295 | const struct ov08x40_mode *cur_mode; |
1296 | |
1297 | /* Mutex for serialized access */ |
1298 | struct mutex mutex; |
1299 | |
1300 | /* True if the device has been identified */ |
1301 | bool identified; |
1302 | }; |
1303 | |
1304 | #define to_ov08x40(_sd) container_of(_sd, struct ov08x40, sd) |
1305 | |
1306 | /* Read registers up to 4 at a time */ |
1307 | static int ov08x40_read_reg(struct ov08x40 *ov08x, |
1308 | u16 reg, u32 len, u32 *val) |
1309 | { |
1310 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1311 | struct i2c_msg msgs[2]; |
1312 | u8 *data_be_p; |
1313 | int ret; |
1314 | __be32 data_be = 0; |
1315 | __be16 reg_addr_be = cpu_to_be16(reg); |
1316 | |
1317 | if (len > 4) |
1318 | return -EINVAL; |
1319 | |
1320 | data_be_p = (u8 *)&data_be; |
1321 | /* Write register address */ |
1322 | msgs[0].addr = client->addr; |
1323 | msgs[0].flags = 0; |
1324 | msgs[0].len = 2; |
1325 | msgs[0].buf = (u8 *)®_addr_be; |
1326 | |
1327 | /* Read data from register */ |
1328 | msgs[1].addr = client->addr; |
1329 | msgs[1].flags = I2C_M_RD; |
1330 | msgs[1].len = len; |
1331 | msgs[1].buf = &data_be_p[4 - len]; |
1332 | |
1333 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
1334 | if (ret != ARRAY_SIZE(msgs)) |
1335 | return -EIO; |
1336 | |
1337 | *val = be32_to_cpu(data_be); |
1338 | |
1339 | return 0; |
1340 | } |
1341 | |
1342 | static int ov08x40_burst_fill_regs(struct ov08x40 *ov08x, u16 first_reg, |
1343 | u16 last_reg, u8 val) |
1344 | { |
1345 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1346 | struct i2c_msg msgs; |
1347 | size_t i, num_regs; |
1348 | int ret; |
1349 | |
1350 | num_regs = last_reg - first_reg + 1; |
1351 | msgs.addr = client->addr; |
1352 | msgs.flags = 0; |
1353 | msgs.len = 2 + num_regs; |
1354 | msgs.buf = kmalloc(size: msgs.len, GFP_KERNEL); |
1355 | |
1356 | if (!msgs.buf) |
1357 | return -ENOMEM; |
1358 | |
1359 | put_unaligned_be16(val: first_reg, p: msgs.buf); |
1360 | |
1361 | for (i = 0; i < num_regs; ++i) |
1362 | msgs.buf[2 + i] = val; |
1363 | |
1364 | ret = i2c_transfer(adap: client->adapter, msgs: &msgs, num: 1); |
1365 | |
1366 | kfree(objp: msgs.buf); |
1367 | |
1368 | if (ret != 1) { |
1369 | dev_err(&client->dev, "Failed regs transferred: %d\n" , ret); |
1370 | return -EIO; |
1371 | } |
1372 | |
1373 | return 0; |
1374 | } |
1375 | |
1376 | /* Write registers up to 4 at a time */ |
1377 | static int ov08x40_write_reg(struct ov08x40 *ov08x, |
1378 | u16 reg, u32 len, u32 __val) |
1379 | { |
1380 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1381 | int buf_i, val_i; |
1382 | u8 buf[6], *val_p; |
1383 | __be32 val; |
1384 | |
1385 | if (len > 4) |
1386 | return -EINVAL; |
1387 | |
1388 | buf[0] = reg >> 8; |
1389 | buf[1] = reg & 0xff; |
1390 | |
1391 | val = cpu_to_be32(__val); |
1392 | val_p = (u8 *)&val; |
1393 | buf_i = 2; |
1394 | val_i = 4 - len; |
1395 | |
1396 | while (val_i < 4) |
1397 | buf[buf_i++] = val_p[val_i++]; |
1398 | |
1399 | if (i2c_master_send(client, buf, count: len + 2) != len + 2) |
1400 | return -EIO; |
1401 | |
1402 | return 0; |
1403 | } |
1404 | |
1405 | /* Write a list of registers */ |
1406 | static int ov08x40_write_regs(struct ov08x40 *ov08x, |
1407 | const struct ov08x40_reg *regs, u32 len) |
1408 | { |
1409 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1410 | int ret; |
1411 | u32 i; |
1412 | |
1413 | for (i = 0; i < len; i++) { |
1414 | ret = ov08x40_write_reg(ov08x, reg: regs[i].address, len: 1, |
1415 | val: regs[i].val); |
1416 | |
1417 | if (ret) { |
1418 | dev_err_ratelimited(&client->dev, |
1419 | "Failed to write reg 0x%4.4x. error = %d\n" , |
1420 | regs[i].address, ret); |
1421 | |
1422 | return ret; |
1423 | } |
1424 | } |
1425 | |
1426 | return 0; |
1427 | } |
1428 | |
1429 | static int ov08x40_write_reg_list(struct ov08x40 *ov08x, |
1430 | const struct ov08x40_reg_list *r_list) |
1431 | { |
1432 | return ov08x40_write_regs(ov08x, regs: r_list->regs, len: r_list->num_of_regs); |
1433 | } |
1434 | |
1435 | static int ov08x40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
1436 | { |
1437 | const struct ov08x40_mode *default_mode = &supported_modes[0]; |
1438 | struct ov08x40 *ov08x = to_ov08x40(sd); |
1439 | struct v4l2_mbus_framefmt *try_fmt = |
1440 | v4l2_subdev_state_get_format(fh->state, 0); |
1441 | |
1442 | mutex_lock(&ov08x->mutex); |
1443 | |
1444 | /* Initialize try_fmt */ |
1445 | try_fmt->width = default_mode->width; |
1446 | try_fmt->height = default_mode->height; |
1447 | try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
1448 | try_fmt->field = V4L2_FIELD_NONE; |
1449 | |
1450 | /* No crop or compose */ |
1451 | mutex_unlock(lock: &ov08x->mutex); |
1452 | |
1453 | return 0; |
1454 | } |
1455 | |
1456 | static int ov08x40_update_digital_gain(struct ov08x40 *ov08x, u32 d_gain) |
1457 | { |
1458 | int ret; |
1459 | u32 val; |
1460 | |
1461 | /* |
1462 | * 0x350C[1:0], 0x350B[7:0], 0x350A[4:0] |
1463 | */ |
1464 | |
1465 | val = (d_gain & OV08X40_DGTL_GAIN_L_MASK) << OV08X40_DGTL_GAIN_L_SHIFT; |
1466 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_L, |
1467 | OV08X40_REG_VALUE_08BIT, val: val); |
1468 | if (ret) |
1469 | return ret; |
1470 | |
1471 | val = (d_gain >> OV08X40_DGTL_GAIN_M_SHIFT) & OV08X40_DGTL_GAIN_M_MASK; |
1472 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_M, |
1473 | OV08X40_REG_VALUE_08BIT, val: val); |
1474 | if (ret) |
1475 | return ret; |
1476 | |
1477 | val = (d_gain >> OV08X40_DGTL_GAIN_H_SHIFT) & OV08X40_DGTL_GAIN_H_MASK; |
1478 | |
1479 | return ov08x40_write_reg(ov08x, OV08X40_REG_DGTL_GAIN_H, |
1480 | OV08X40_REG_VALUE_08BIT, val: val); |
1481 | } |
1482 | |
1483 | static int ov08x40_enable_test_pattern(struct ov08x40 *ov08x, u32 pattern) |
1484 | { |
1485 | int ret; |
1486 | u32 val; |
1487 | |
1488 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_TEST_PATTERN, |
1489 | OV08X40_REG_VALUE_08BIT, val: &val); |
1490 | if (ret) |
1491 | return ret; |
1492 | |
1493 | if (pattern) { |
1494 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_ISP, |
1495 | OV08X40_REG_VALUE_08BIT, val: &val); |
1496 | if (ret) |
1497 | return ret; |
1498 | |
1499 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_ISP, |
1500 | OV08X40_REG_VALUE_08BIT, |
1501 | val: val | BIT(1)); |
1502 | if (ret) |
1503 | return ret; |
1504 | |
1505 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_SHORT_TEST_PATTERN, |
1506 | OV08X40_REG_VALUE_08BIT, val: &val); |
1507 | if (ret) |
1508 | return ret; |
1509 | |
1510 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_SHORT_TEST_PATTERN, |
1511 | OV08X40_REG_VALUE_08BIT, |
1512 | val: val | BIT(0)); |
1513 | if (ret) |
1514 | return ret; |
1515 | |
1516 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_TEST_PATTERN, |
1517 | OV08X40_REG_VALUE_08BIT, val: &val); |
1518 | if (ret) |
1519 | return ret; |
1520 | |
1521 | val &= OV08X40_TEST_PATTERN_MASK; |
1522 | val |= ((pattern - 1) << OV08X40_TEST_PATTERN_BAR_SHIFT) | |
1523 | OV08X40_TEST_PATTERN_ENABLE; |
1524 | } else { |
1525 | val &= ~OV08X40_TEST_PATTERN_ENABLE; |
1526 | } |
1527 | |
1528 | return ov08x40_write_reg(ov08x, OV08X40_REG_TEST_PATTERN, |
1529 | OV08X40_REG_VALUE_08BIT, val: val); |
1530 | } |
1531 | |
1532 | static int ov08x40_set_ctrl_hflip(struct ov08x40 *ov08x, u32 ctrl_val) |
1533 | { |
1534 | int ret; |
1535 | u32 val; |
1536 | |
1537 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_MIRROR, |
1538 | OV08X40_REG_VALUE_08BIT, val: &val); |
1539 | if (ret) |
1540 | return ret; |
1541 | |
1542 | return ov08x40_write_reg(ov08x, OV08X40_REG_MIRROR, |
1543 | OV08X40_REG_VALUE_08BIT, |
1544 | val: ctrl_val ? val | BIT(2) : val & ~BIT(2)); |
1545 | } |
1546 | |
1547 | static int ov08x40_set_ctrl_vflip(struct ov08x40 *ov08x, u32 ctrl_val) |
1548 | { |
1549 | int ret; |
1550 | u32 val; |
1551 | |
1552 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_VFLIP, |
1553 | OV08X40_REG_VALUE_08BIT, val: &val); |
1554 | if (ret) |
1555 | return ret; |
1556 | |
1557 | return ov08x40_write_reg(ov08x, OV08X40_REG_VFLIP, |
1558 | OV08X40_REG_VALUE_08BIT, |
1559 | val: ctrl_val ? val | BIT(2) : val & ~BIT(2)); |
1560 | } |
1561 | |
1562 | static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) |
1563 | { |
1564 | struct ov08x40 *ov08x = container_of(ctrl->handler, |
1565 | struct ov08x40, ctrl_handler); |
1566 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1567 | s64 max; |
1568 | int exp; |
1569 | int fll; |
1570 | int ret = 0; |
1571 | |
1572 | /* Propagate change of current control to all related controls */ |
1573 | switch (ctrl->id) { |
1574 | case V4L2_CID_VBLANK: |
1575 | /* Update max exposure while meeting expected vblanking */ |
1576 | /* |
1577 | * because in normal mode, 1 HTS = 0.5 tline |
1578 | * fps = sclk / hts / vts |
1579 | * so the vts value needs to be double |
1580 | */ |
1581 | max = ((ov08x->cur_mode->height + ctrl->val) << |
1582 | ov08x->cur_mode->exposure_shift) - |
1583 | ov08x->cur_mode->exposure_margin; |
1584 | |
1585 | __v4l2_ctrl_modify_range(ctrl: ov08x->exposure, |
1586 | min: ov08x->exposure->minimum, |
1587 | max, step: ov08x->exposure->step, def: max); |
1588 | break; |
1589 | } |
1590 | |
1591 | /* |
1592 | * Applying V4L2 control value only happens |
1593 | * when power is up for streaming |
1594 | */ |
1595 | if (!pm_runtime_get_if_in_use(dev: &client->dev)) |
1596 | return 0; |
1597 | |
1598 | switch (ctrl->id) { |
1599 | case V4L2_CID_ANALOGUE_GAIN: |
1600 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_ANALOG_GAIN, |
1601 | OV08X40_REG_VALUE_16BIT, |
1602 | val: ctrl->val << 1); |
1603 | break; |
1604 | case V4L2_CID_DIGITAL_GAIN: |
1605 | ret = ov08x40_update_digital_gain(ov08x, d_gain: ctrl->val); |
1606 | break; |
1607 | case V4L2_CID_EXPOSURE: |
1608 | exp = (ctrl->val << ov08x->cur_mode->exposure_shift) - |
1609 | ov08x->cur_mode->exposure_margin; |
1610 | |
1611 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_EXPOSURE, |
1612 | OV08X40_REG_VALUE_24BIT, |
1613 | val: exp); |
1614 | break; |
1615 | case V4L2_CID_VBLANK: |
1616 | fll = ((ov08x->cur_mode->height + ctrl->val) << |
1617 | ov08x->cur_mode->exposure_shift); |
1618 | |
1619 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_VTS, |
1620 | OV08X40_REG_VALUE_16BIT, |
1621 | val: fll); |
1622 | break; |
1623 | case V4L2_CID_TEST_PATTERN: |
1624 | ret = ov08x40_enable_test_pattern(ov08x, pattern: ctrl->val); |
1625 | break; |
1626 | case V4L2_CID_HFLIP: |
1627 | ov08x40_set_ctrl_hflip(ov08x, ctrl_val: ctrl->val); |
1628 | break; |
1629 | case V4L2_CID_VFLIP: |
1630 | ov08x40_set_ctrl_vflip(ov08x, ctrl_val: ctrl->val); |
1631 | break; |
1632 | default: |
1633 | dev_info(&client->dev, |
1634 | "ctrl(id:0x%x,val:0x%x) is not handled\n" , |
1635 | ctrl->id, ctrl->val); |
1636 | break; |
1637 | } |
1638 | |
1639 | pm_runtime_put(dev: &client->dev); |
1640 | |
1641 | return ret; |
1642 | } |
1643 | |
1644 | static const struct v4l2_ctrl_ops ov08x40_ctrl_ops = { |
1645 | .s_ctrl = ov08x40_set_ctrl, |
1646 | }; |
1647 | |
1648 | static int ov08x40_enum_mbus_code(struct v4l2_subdev *sd, |
1649 | struct v4l2_subdev_state *sd_state, |
1650 | struct v4l2_subdev_mbus_code_enum *code) |
1651 | { |
1652 | /* Only one bayer order(GRBG) is supported */ |
1653 | if (code->index > 0) |
1654 | return -EINVAL; |
1655 | |
1656 | code->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
1657 | |
1658 | return 0; |
1659 | } |
1660 | |
1661 | static int ov08x40_enum_frame_size(struct v4l2_subdev *sd, |
1662 | struct v4l2_subdev_state *sd_state, |
1663 | struct v4l2_subdev_frame_size_enum *fse) |
1664 | { |
1665 | if (fse->index >= ARRAY_SIZE(supported_modes)) |
1666 | return -EINVAL; |
1667 | |
1668 | if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) |
1669 | return -EINVAL; |
1670 | |
1671 | fse->min_width = supported_modes[fse->index].width; |
1672 | fse->max_width = fse->min_width; |
1673 | fse->min_height = supported_modes[fse->index].height; |
1674 | fse->max_height = fse->min_height; |
1675 | |
1676 | return 0; |
1677 | } |
1678 | |
1679 | static void ov08x40_update_pad_format(const struct ov08x40_mode *mode, |
1680 | struct v4l2_subdev_format *fmt) |
1681 | { |
1682 | fmt->format.width = mode->width; |
1683 | fmt->format.height = mode->height; |
1684 | fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
1685 | fmt->format.field = V4L2_FIELD_NONE; |
1686 | } |
1687 | |
1688 | static int ov08x40_do_get_pad_format(struct ov08x40 *ov08x, |
1689 | struct v4l2_subdev_state *sd_state, |
1690 | struct v4l2_subdev_format *fmt) |
1691 | { |
1692 | struct v4l2_mbus_framefmt *framefmt; |
1693 | |
1694 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { |
1695 | framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); |
1696 | fmt->format = *framefmt; |
1697 | } else { |
1698 | ov08x40_update_pad_format(mode: ov08x->cur_mode, fmt); |
1699 | } |
1700 | |
1701 | return 0; |
1702 | } |
1703 | |
1704 | static int ov08x40_get_pad_format(struct v4l2_subdev *sd, |
1705 | struct v4l2_subdev_state *sd_state, |
1706 | struct v4l2_subdev_format *fmt) |
1707 | { |
1708 | struct ov08x40 *ov08x = to_ov08x40(sd); |
1709 | int ret; |
1710 | |
1711 | mutex_lock(&ov08x->mutex); |
1712 | ret = ov08x40_do_get_pad_format(ov08x, sd_state, fmt); |
1713 | mutex_unlock(lock: &ov08x->mutex); |
1714 | |
1715 | return ret; |
1716 | } |
1717 | |
1718 | static int |
1719 | ov08x40_set_pad_format(struct v4l2_subdev *sd, |
1720 | struct v4l2_subdev_state *sd_state, |
1721 | struct v4l2_subdev_format *fmt) |
1722 | { |
1723 | struct ov08x40 *ov08x = to_ov08x40(sd); |
1724 | const struct ov08x40_mode *mode; |
1725 | struct v4l2_mbus_framefmt *framefmt; |
1726 | s32 vblank_def; |
1727 | s32 vblank_min; |
1728 | s64 h_blank; |
1729 | s64 pixel_rate; |
1730 | s64 link_freq; |
1731 | u64 steps; |
1732 | |
1733 | mutex_lock(&ov08x->mutex); |
1734 | |
1735 | /* Only one raw bayer(GRBG) order is supported */ |
1736 | if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) |
1737 | fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
1738 | |
1739 | mode = v4l2_find_nearest_size(supported_modes, |
1740 | ARRAY_SIZE(supported_modes), |
1741 | width, height, |
1742 | fmt->format.width, fmt->format.height); |
1743 | ov08x40_update_pad_format(mode, fmt); |
1744 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { |
1745 | framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); |
1746 | *framefmt = fmt->format; |
1747 | } else { |
1748 | ov08x->cur_mode = mode; |
1749 | __v4l2_ctrl_s_ctrl(ctrl: ov08x->link_freq, val: mode->link_freq_index); |
1750 | link_freq = link_freq_menu_items[mode->link_freq_index]; |
1751 | pixel_rate = link_freq_to_pixel_rate(f: link_freq); |
1752 | __v4l2_ctrl_s_ctrl_int64(ctrl: ov08x->pixel_rate, val: pixel_rate); |
1753 | |
1754 | /* Update limits and set FPS to default */ |
1755 | vblank_def = ov08x->cur_mode->vts_def - |
1756 | ov08x->cur_mode->height; |
1757 | vblank_min = ov08x->cur_mode->vts_min - |
1758 | ov08x->cur_mode->height; |
1759 | |
1760 | /* |
1761 | * The frame length line should be aligned to a multiple of 4, |
1762 | * as provided by the sensor vendor, in normal mode. |
1763 | */ |
1764 | steps = mode->exposure_shift == 1 ? 4 : 1; |
1765 | |
1766 | __v4l2_ctrl_modify_range(ctrl: ov08x->vblank, min: vblank_min, |
1767 | OV08X40_VTS_MAX |
1768 | - ov08x->cur_mode->height, |
1769 | step: steps, |
1770 | def: vblank_def); |
1771 | __v4l2_ctrl_s_ctrl(ctrl: ov08x->vblank, val: vblank_def); |
1772 | |
1773 | h_blank = ov08x->cur_mode->llp - ov08x->cur_mode->width; |
1774 | |
1775 | __v4l2_ctrl_modify_range(ctrl: ov08x->hblank, min: h_blank, |
1776 | max: h_blank, step: 1, def: h_blank); |
1777 | } |
1778 | |
1779 | mutex_unlock(lock: &ov08x->mutex); |
1780 | |
1781 | return 0; |
1782 | } |
1783 | |
1784 | static int ov08x40_start_streaming(struct ov08x40 *ov08x) |
1785 | { |
1786 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1787 | const struct ov08x40_reg_list *reg_list; |
1788 | int ret, link_freq_index; |
1789 | |
1790 | /* Get out of from software reset */ |
1791 | ret = ov08x40_write_reg(ov08x, OV08X40_REG_SOFTWARE_RST, |
1792 | OV08X40_REG_VALUE_08BIT, OV08X40_SOFTWARE_RST); |
1793 | if (ret) { |
1794 | dev_err(&client->dev, "%s failed to set powerup registers\n" , |
1795 | __func__); |
1796 | return ret; |
1797 | } |
1798 | |
1799 | link_freq_index = ov08x->cur_mode->link_freq_index; |
1800 | reg_list = &link_freq_configs[link_freq_index].reg_list; |
1801 | |
1802 | ret = ov08x40_write_reg_list(ov08x, r_list: reg_list); |
1803 | if (ret) { |
1804 | dev_err(&client->dev, "%s failed to set plls\n" , __func__); |
1805 | return ret; |
1806 | } |
1807 | |
1808 | /* Apply default values of current mode */ |
1809 | reg_list = &ov08x->cur_mode->reg_list; |
1810 | ret = ov08x40_write_reg_list(ov08x, r_list: reg_list); |
1811 | if (ret) { |
1812 | dev_err(&client->dev, "%s failed to set mode\n" , __func__); |
1813 | return ret; |
1814 | } |
1815 | |
1816 | /* Use i2c burst to write register on full size registers */ |
1817 | if (ov08x->cur_mode->exposure_shift == 1) { |
1818 | ret = ov08x40_burst_fill_regs(ov08x, OV08X40_REG_XTALK_FIRST_A, |
1819 | OV08X40_REG_XTALK_LAST_A, val: 0x75); |
1820 | if (ret == 0) |
1821 | ret = ov08x40_burst_fill_regs(ov08x, |
1822 | OV08X40_REG_XTALK_FIRST_B, |
1823 | OV08X40_REG_XTALK_LAST_B, |
1824 | val: 0x75); |
1825 | } |
1826 | |
1827 | if (ret) { |
1828 | dev_err(&client->dev, "%s failed to set regs\n" , __func__); |
1829 | return ret; |
1830 | } |
1831 | |
1832 | /* Apply customized values from user */ |
1833 | ret = __v4l2_ctrl_handler_setup(hdl: ov08x->sd.ctrl_handler); |
1834 | if (ret) |
1835 | return ret; |
1836 | |
1837 | return ov08x40_write_reg(ov08x, OV08X40_REG_MODE_SELECT, |
1838 | OV08X40_REG_VALUE_08BIT, |
1839 | OV08X40_MODE_STREAMING); |
1840 | } |
1841 | |
1842 | /* Stop streaming */ |
1843 | static int ov08x40_stop_streaming(struct ov08x40 *ov08x) |
1844 | { |
1845 | return ov08x40_write_reg(ov08x, OV08X40_REG_MODE_SELECT, |
1846 | OV08X40_REG_VALUE_08BIT, OV08X40_MODE_STANDBY); |
1847 | } |
1848 | |
1849 | static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable) |
1850 | { |
1851 | struct ov08x40 *ov08x = to_ov08x40(sd); |
1852 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
1853 | int ret = 0; |
1854 | |
1855 | mutex_lock(&ov08x->mutex); |
1856 | |
1857 | if (enable) { |
1858 | ret = pm_runtime_resume_and_get(dev: &client->dev); |
1859 | if (ret < 0) |
1860 | goto err_unlock; |
1861 | |
1862 | /* |
1863 | * Apply default & customized values |
1864 | * and then start streaming. |
1865 | */ |
1866 | ret = ov08x40_start_streaming(ov08x); |
1867 | if (ret) |
1868 | goto err_rpm_put; |
1869 | } else { |
1870 | ov08x40_stop_streaming(ov08x); |
1871 | pm_runtime_put(dev: &client->dev); |
1872 | } |
1873 | |
1874 | mutex_unlock(lock: &ov08x->mutex); |
1875 | |
1876 | return ret; |
1877 | |
1878 | err_rpm_put: |
1879 | pm_runtime_put(dev: &client->dev); |
1880 | err_unlock: |
1881 | mutex_unlock(lock: &ov08x->mutex); |
1882 | |
1883 | return ret; |
1884 | } |
1885 | |
1886 | /* Verify chip ID */ |
1887 | static int ov08x40_identify_module(struct ov08x40 *ov08x) |
1888 | { |
1889 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1890 | int ret; |
1891 | u32 val; |
1892 | |
1893 | if (ov08x->identified) |
1894 | return 0; |
1895 | |
1896 | ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID, |
1897 | OV08X40_REG_VALUE_24BIT, val: &val); |
1898 | if (ret) |
1899 | return ret; |
1900 | |
1901 | if (val != OV08X40_CHIP_ID) { |
1902 | dev_err(&client->dev, "chip id mismatch: %x!=%x\n" , |
1903 | OV08X40_CHIP_ID, val); |
1904 | return -ENXIO; |
1905 | } |
1906 | |
1907 | ov08x->identified = true; |
1908 | |
1909 | return 0; |
1910 | } |
1911 | |
1912 | static const struct v4l2_subdev_video_ops ov08x40_video_ops = { |
1913 | .s_stream = ov08x40_set_stream, |
1914 | }; |
1915 | |
1916 | static const struct v4l2_subdev_pad_ops ov08x40_pad_ops = { |
1917 | .enum_mbus_code = ov08x40_enum_mbus_code, |
1918 | .get_fmt = ov08x40_get_pad_format, |
1919 | .set_fmt = ov08x40_set_pad_format, |
1920 | .enum_frame_size = ov08x40_enum_frame_size, |
1921 | }; |
1922 | |
1923 | static const struct v4l2_subdev_ops ov08x40_subdev_ops = { |
1924 | .video = &ov08x40_video_ops, |
1925 | .pad = &ov08x40_pad_ops, |
1926 | }; |
1927 | |
1928 | static const struct media_entity_operations ov08x40_subdev_entity_ops = { |
1929 | .link_validate = v4l2_subdev_link_validate, |
1930 | }; |
1931 | |
1932 | static const struct v4l2_subdev_internal_ops ov08x40_internal_ops = { |
1933 | .open = ov08x40_open, |
1934 | }; |
1935 | |
1936 | static int ov08x40_init_controls(struct ov08x40 *ov08x) |
1937 | { |
1938 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov08x->sd); |
1939 | struct v4l2_fwnode_device_properties props; |
1940 | struct v4l2_ctrl_handler *ctrl_hdlr; |
1941 | s64 exposure_max; |
1942 | s64 vblank_def; |
1943 | s64 vblank_min; |
1944 | s64 hblank; |
1945 | s64 pixel_rate_min; |
1946 | s64 pixel_rate_max; |
1947 | const struct ov08x40_mode *mode; |
1948 | u32 max; |
1949 | int ret; |
1950 | |
1951 | ctrl_hdlr = &ov08x->ctrl_handler; |
1952 | ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); |
1953 | if (ret) |
1954 | return ret; |
1955 | |
1956 | mutex_init(&ov08x->mutex); |
1957 | ctrl_hdlr->lock = &ov08x->mutex; |
1958 | max = ARRAY_SIZE(link_freq_menu_items) - 1; |
1959 | ov08x->link_freq = v4l2_ctrl_new_int_menu(hdl: ctrl_hdlr, |
1960 | ops: &ov08x40_ctrl_ops, |
1961 | V4L2_CID_LINK_FREQ, |
1962 | max, |
1963 | def: 0, |
1964 | qmenu_int: link_freq_menu_items); |
1965 | if (ov08x->link_freq) |
1966 | ov08x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
1967 | |
1968 | pixel_rate_max = link_freq_to_pixel_rate(f: link_freq_menu_items[0]); |
1969 | pixel_rate_min = 0; |
1970 | /* By default, PIXEL_RATE is read only */ |
1971 | ov08x->pixel_rate = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
1972 | V4L2_CID_PIXEL_RATE, |
1973 | min: pixel_rate_min, max: pixel_rate_max, |
1974 | step: 1, def: pixel_rate_max); |
1975 | |
1976 | mode = ov08x->cur_mode; |
1977 | vblank_def = mode->vts_def - mode->height; |
1978 | vblank_min = mode->vts_min - mode->height; |
1979 | ov08x->vblank = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
1980 | V4L2_CID_VBLANK, |
1981 | min: vblank_min, |
1982 | OV08X40_VTS_MAX - mode->height, step: 1, |
1983 | def: vblank_def); |
1984 | |
1985 | hblank = ov08x->cur_mode->llp - ov08x->cur_mode->width; |
1986 | |
1987 | ov08x->hblank = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
1988 | V4L2_CID_HBLANK, |
1989 | min: hblank, max: hblank, step: 1, def: hblank); |
1990 | if (ov08x->hblank) |
1991 | ov08x->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
1992 | |
1993 | exposure_max = mode->vts_def - OV08X40_EXPOSURE_MAX_MARGIN; |
1994 | ov08x->exposure = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
1995 | V4L2_CID_EXPOSURE, |
1996 | OV08X40_EXPOSURE_MIN, |
1997 | max: exposure_max, OV08X40_EXPOSURE_STEP, |
1998 | def: exposure_max); |
1999 | |
2000 | v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, |
2001 | OV08X40_ANA_GAIN_MIN, OV08X40_ANA_GAIN_MAX, |
2002 | OV08X40_ANA_GAIN_STEP, OV08X40_ANA_GAIN_DEFAULT); |
2003 | |
2004 | /* Digital gain */ |
2005 | v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, V4L2_CID_DIGITAL_GAIN, |
2006 | OV08X40_DGTL_GAIN_MIN, OV08X40_DGTL_GAIN_MAX, |
2007 | OV08X40_DGTL_GAIN_STEP, OV08X40_DGTL_GAIN_DEFAULT); |
2008 | |
2009 | v4l2_ctrl_new_std_menu_items(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
2010 | V4L2_CID_TEST_PATTERN, |
2011 | ARRAY_SIZE(ov08x40_test_pattern_menu) - 1, |
2012 | mask: 0, def: 0, qmenu: ov08x40_test_pattern_menu); |
2013 | |
2014 | v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
2015 | V4L2_CID_HFLIP, min: 0, max: 1, step: 1, def: 0); |
2016 | v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov08x40_ctrl_ops, |
2017 | V4L2_CID_VFLIP, min: 0, max: 1, step: 1, def: 0); |
2018 | |
2019 | if (ctrl_hdlr->error) { |
2020 | ret = ctrl_hdlr->error; |
2021 | dev_err(&client->dev, "%s control init failed (%d)\n" , |
2022 | __func__, ret); |
2023 | goto error; |
2024 | } |
2025 | |
2026 | ret = v4l2_fwnode_device_parse(dev: &client->dev, props: &props); |
2027 | if (ret) |
2028 | goto error; |
2029 | |
2030 | ret = v4l2_ctrl_new_fwnode_properties(hdl: ctrl_hdlr, ctrl_ops: &ov08x40_ctrl_ops, |
2031 | p: &props); |
2032 | if (ret) |
2033 | goto error; |
2034 | |
2035 | ov08x->sd.ctrl_handler = ctrl_hdlr; |
2036 | |
2037 | return 0; |
2038 | |
2039 | error: |
2040 | v4l2_ctrl_handler_free(hdl: ctrl_hdlr); |
2041 | mutex_destroy(lock: &ov08x->mutex); |
2042 | |
2043 | return ret; |
2044 | } |
2045 | |
2046 | static void ov08x40_free_controls(struct ov08x40 *ov08x) |
2047 | { |
2048 | v4l2_ctrl_handler_free(hdl: ov08x->sd.ctrl_handler); |
2049 | mutex_destroy(lock: &ov08x->mutex); |
2050 | } |
2051 | |
2052 | static int ov08x40_check_hwcfg(struct device *dev) |
2053 | { |
2054 | struct v4l2_fwnode_endpoint bus_cfg = { |
2055 | .bus_type = V4L2_MBUS_CSI2_DPHY |
2056 | }; |
2057 | struct fwnode_handle *ep; |
2058 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
2059 | unsigned int i, j; |
2060 | int ret; |
2061 | u32 ext_clk; |
2062 | |
2063 | if (!fwnode) |
2064 | return -ENXIO; |
2065 | |
2066 | ret = fwnode_property_read_u32(dev_fwnode(dev), propname: "clock-frequency" , |
2067 | val: &ext_clk); |
2068 | if (ret) { |
2069 | dev_err(dev, "can't get clock frequency" ); |
2070 | return ret; |
2071 | } |
2072 | |
2073 | if (ext_clk != OV08X40_EXT_CLK) { |
2074 | dev_err(dev, "external clock %d is not supported" , |
2075 | ext_clk); |
2076 | return -EINVAL; |
2077 | } |
2078 | |
2079 | ep = fwnode_graph_get_next_endpoint(fwnode, NULL); |
2080 | if (!ep) |
2081 | return -ENXIO; |
2082 | |
2083 | ret = v4l2_fwnode_endpoint_alloc_parse(fwnode: ep, vep: &bus_cfg); |
2084 | fwnode_handle_put(fwnode: ep); |
2085 | if (ret) |
2086 | return ret; |
2087 | |
2088 | if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) { |
2089 | dev_err(dev, "number of CSI2 data lanes %d is not supported" , |
2090 | bus_cfg.bus.mipi_csi2.num_data_lanes); |
2091 | ret = -EINVAL; |
2092 | goto out_err; |
2093 | } |
2094 | |
2095 | if (!bus_cfg.nr_of_link_frequencies) { |
2096 | dev_err(dev, "no link frequencies defined" ); |
2097 | ret = -EINVAL; |
2098 | goto out_err; |
2099 | } |
2100 | |
2101 | for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { |
2102 | for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { |
2103 | if (link_freq_menu_items[i] == |
2104 | bus_cfg.link_frequencies[j]) |
2105 | break; |
2106 | } |
2107 | |
2108 | if (j == bus_cfg.nr_of_link_frequencies) { |
2109 | dev_err(dev, "no link frequency %lld supported" , |
2110 | link_freq_menu_items[i]); |
2111 | ret = -EINVAL; |
2112 | goto out_err; |
2113 | } |
2114 | } |
2115 | |
2116 | out_err: |
2117 | v4l2_fwnode_endpoint_free(vep: &bus_cfg); |
2118 | |
2119 | return ret; |
2120 | } |
2121 | |
2122 | static int ov08x40_probe(struct i2c_client *client) |
2123 | { |
2124 | struct ov08x40 *ov08x; |
2125 | int ret; |
2126 | bool full_power; |
2127 | |
2128 | /* Check HW config */ |
2129 | ret = ov08x40_check_hwcfg(dev: &client->dev); |
2130 | if (ret) { |
2131 | dev_err(&client->dev, "failed to check hwcfg: %d" , ret); |
2132 | return ret; |
2133 | } |
2134 | |
2135 | ov08x = devm_kzalloc(dev: &client->dev, size: sizeof(*ov08x), GFP_KERNEL); |
2136 | if (!ov08x) |
2137 | return -ENOMEM; |
2138 | |
2139 | /* Initialize subdev */ |
2140 | v4l2_i2c_subdev_init(sd: &ov08x->sd, client, ops: &ov08x40_subdev_ops); |
2141 | |
2142 | full_power = acpi_dev_state_d0(dev: &client->dev); |
2143 | if (full_power) { |
2144 | /* Check module identity */ |
2145 | ret = ov08x40_identify_module(ov08x); |
2146 | if (ret) { |
2147 | dev_err(&client->dev, "failed to find sensor: %d\n" , ret); |
2148 | return ret; |
2149 | } |
2150 | } |
2151 | |
2152 | /* Set default mode to max resolution */ |
2153 | ov08x->cur_mode = &supported_modes[0]; |
2154 | |
2155 | ret = ov08x40_init_controls(ov08x); |
2156 | if (ret) |
2157 | return ret; |
2158 | |
2159 | /* Initialize subdev */ |
2160 | ov08x->sd.internal_ops = &ov08x40_internal_ops; |
2161 | ov08x->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
2162 | ov08x->sd.entity.ops = &ov08x40_subdev_entity_ops; |
2163 | ov08x->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; |
2164 | |
2165 | /* Initialize source pad */ |
2166 | ov08x->pad.flags = MEDIA_PAD_FL_SOURCE; |
2167 | ret = media_entity_pads_init(entity: &ov08x->sd.entity, num_pads: 1, pads: &ov08x->pad); |
2168 | if (ret) { |
2169 | dev_err(&client->dev, "%s failed:%d\n" , __func__, ret); |
2170 | goto error_handler_free; |
2171 | } |
2172 | |
2173 | ret = v4l2_async_register_subdev_sensor(sd: &ov08x->sd); |
2174 | if (ret < 0) |
2175 | goto error_media_entity; |
2176 | |
2177 | if (full_power) |
2178 | pm_runtime_set_active(dev: &client->dev); |
2179 | pm_runtime_enable(dev: &client->dev); |
2180 | pm_runtime_idle(dev: &client->dev); |
2181 | |
2182 | return 0; |
2183 | |
2184 | error_media_entity: |
2185 | media_entity_cleanup(entity: &ov08x->sd.entity); |
2186 | |
2187 | error_handler_free: |
2188 | ov08x40_free_controls(ov08x); |
2189 | |
2190 | return ret; |
2191 | } |
2192 | |
2193 | static void ov08x40_remove(struct i2c_client *client) |
2194 | { |
2195 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
2196 | struct ov08x40 *ov08x = to_ov08x40(sd); |
2197 | |
2198 | v4l2_async_unregister_subdev(sd); |
2199 | media_entity_cleanup(entity: &sd->entity); |
2200 | ov08x40_free_controls(ov08x); |
2201 | |
2202 | pm_runtime_disable(dev: &client->dev); |
2203 | pm_runtime_set_suspended(dev: &client->dev); |
2204 | } |
2205 | |
2206 | #ifdef CONFIG_ACPI |
2207 | static const struct acpi_device_id ov08x40_acpi_ids[] = { |
2208 | {"OVTI08F4" }, |
2209 | { /* sentinel */ } |
2210 | }; |
2211 | |
2212 | MODULE_DEVICE_TABLE(acpi, ov08x40_acpi_ids); |
2213 | #endif |
2214 | |
2215 | static struct i2c_driver ov08x40_i2c_driver = { |
2216 | .driver = { |
2217 | .name = "ov08x40" , |
2218 | .acpi_match_table = ACPI_PTR(ov08x40_acpi_ids), |
2219 | }, |
2220 | .probe = ov08x40_probe, |
2221 | .remove = ov08x40_remove, |
2222 | .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, |
2223 | }; |
2224 | |
2225 | module_i2c_driver(ov08x40_i2c_driver); |
2226 | |
2227 | MODULE_AUTHOR("Jason Chen <jason.z.chen@intel.com>" ); |
2228 | MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>" ); |
2229 | MODULE_AUTHOR("Shawn Tu" ); |
2230 | MODULE_DESCRIPTION("OmniVision OV08X40 sensor driver" ); |
2231 | MODULE_LICENSE("GPL" ); |
2232 | |