1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for GalaxyCore gc08a3 image sensor
5 * Copyright 2024 MediaTek
7 * Zhi Mao <zhi.mao@mediatek.com>
9 #include <linux/array_size.h>
10 #include <linux/bits.h>
11 #include <linux/clk.h>
12 #include <linux/container_of.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/err.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/math64.h>
18 #include <linux/mod_devicetable.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/property.h>
21 #include <linux/regulator/consumer.h>
22 #include <linux/types.h>
23 #include <linux/units.h>
25 #include <media/v4l2-cci.h>
26 #include <media/v4l2-ctrls.h>
27 #include <media/v4l2-fwnode.h>
28 #include <media/v4l2-subdev.h>
30 #define GC08A3_REG_TEST_PATTERN_EN CCI_REG8(0x008c)
31 #define GC08A3_REG_TEST_PATTERN_IDX CCI_REG8(0x008d)
32 #define GC08A3_TEST_PATTERN_EN 0x01
34 #define GC08A3_STREAMING_REG CCI_REG8(0x0100)
36 #define GC08A3_FLIP_REG CCI_REG8(0x0101)
37 #define GC08A3_FLIP_H_MASK BIT(0)
38 #define GC08A3_FLIP_V_MASK BIT(1)
40 #define GC08A3_EXP_REG CCI_REG16(0x0202)
41 #define GC08A3_EXP_MARGIN 16
42 #define GC08A3_EXP_MIN 4
43 #define GC08A3_EXP_STEP 1
45 #define GC08A3_AGAIN_REG CCI_REG16(0x0204)
46 #define GC08A3_AGAIN_MIN 1024
47 #define GC08A3_AGAIN_MAX (1024 * 16)
48 #define GC08A3_AGAIN_STEP 1
50 #define GC08A3_FRAME_LENGTH_REG CCI_REG16(0x0340)
51 #define GC08A3_VTS_MAX 0xfff0
53 #define GC08A3_REG_CHIP_ID CCI_REG16(0x03f0)
54 #define GC08A3_CHIP_ID 0x08a3
56 #define GC08A3_NATIVE_WIDTH 3264
57 #define GC08A3_NATIVE_HEIGHT 2448
59 #define GC08A3_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ)
60 #define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10
61 #define GC08A3_DATA_LANES 4
63 #define GC08A3_RGB_DEPTH 10
65 #define GC08A3_SLEEP_US (2 * USEC_PER_MSEC)
67 static const char *const gc08a3_test_pattern_menu
[] = {
68 "No Pattern", "Solid Black", "Colour Bar", "Solid White",
69 "Solid Red", "Solid Green", "Solid Blue", "Solid Yellow",
72 static const s64 gc08a3_link_freq_menu_items
[] = {
77 static const char *const gc08a3_supply_name
[] = {
85 struct v4l2_subdev sd
;
89 struct regulator_bulk_data supplies
[ARRAY_SIZE(gc08a3_supply_name
)];
90 struct gpio_desc
*reset_gpio
;
92 struct v4l2_ctrl_handler ctrls
;
93 struct v4l2_ctrl
*pixel_rate
;
94 struct v4l2_ctrl
*link_freq
;
95 struct v4l2_ctrl
*exposure
;
96 struct v4l2_ctrl
*vblank
;
97 struct v4l2_ctrl
*hblank
;
98 struct v4l2_ctrl
*hflip
;
99 struct v4l2_ctrl
*vflip
;
101 struct regmap
*regmap
;
102 unsigned long link_freq_bitmap
;
103 const struct gc08a3_mode
*cur_mode
;
106 struct gc08a3_reg_list
{
108 const struct cci_reg_sequence
*regs
;
111 static const struct cci_reg_sequence mode_3264x2448
[] = {
113 { CCI_REG8(0x0336), 0x70 },
114 { CCI_REG8(0x0383), 0xbb },
115 { CCI_REG8(0x0344), 0x00 },
116 { CCI_REG8(0x0345), 0x06 },
117 { CCI_REG8(0x0346), 0x00 },
118 { CCI_REG8(0x0347), 0x04 },
119 { CCI_REG8(0x0348), 0x0c },
120 { CCI_REG8(0x0349), 0xd0 },
121 { CCI_REG8(0x034a), 0x09 },
122 { CCI_REG8(0x034b), 0x9c },
123 { CCI_REG8(0x0202), 0x09 },
124 { CCI_REG8(0x0203), 0x04 },
125 { CCI_REG8(0x0340), 0x09 },
126 { CCI_REG8(0x0341), 0xf4 },
127 { CCI_REG8(0x0342), 0x07 },
128 { CCI_REG8(0x0343), 0x1c },
130 { CCI_REG8(0x0226), 0x00 },
131 { CCI_REG8(0x0227), 0x28 },
132 { CCI_REG8(0x0e38), 0x49 },
133 { CCI_REG8(0x0210), 0x13 },
134 { CCI_REG8(0x0218), 0x00 },
135 { CCI_REG8(0x0241), 0x88 },
136 { CCI_REG8(0x0392), 0x60 },
139 { CCI_REG8(0x00a2), 0x00 },
140 { CCI_REG8(0x00a3), 0x00 },
141 { CCI_REG8(0x00ab), 0x00 },
142 { CCI_REG8(0x00ac), 0x00 },
145 { CCI_REG8(0x0204), 0x04 },
146 { CCI_REG8(0x0205), 0x00 },
147 { CCI_REG8(0x0050), 0x5c },
148 { CCI_REG8(0x0051), 0x44 },
151 { CCI_REG8(0x009a), 0x66 },
152 { CCI_REG8(0x0351), 0x00 },
153 { CCI_REG8(0x0352), 0x06 },
154 { CCI_REG8(0x0353), 0x00 },
155 { CCI_REG8(0x0354), 0x08 },
156 { CCI_REG8(0x034c), 0x0c },
157 { CCI_REG8(0x034d), 0xc0 },
158 { CCI_REG8(0x034e), 0x09 },
159 { CCI_REG8(0x034f), 0x90 },
162 { CCI_REG8(0x0114), 0x03 },
163 { CCI_REG8(0x0180), 0x65 },
164 { CCI_REG8(0x0181), 0xf0 },
165 { CCI_REG8(0x0185), 0x01 },
166 { CCI_REG8(0x0115), 0x30 },
167 { CCI_REG8(0x011b), 0x12 },
168 { CCI_REG8(0x011c), 0x12 },
169 { CCI_REG8(0x0121), 0x06 },
170 { CCI_REG8(0x0122), 0x06 },
171 { CCI_REG8(0x0123), 0x15 },
172 { CCI_REG8(0x0124), 0x01 },
173 { CCI_REG8(0x0125), 0x0b },
174 { CCI_REG8(0x0126), 0x08 },
175 { CCI_REG8(0x0129), 0x06 },
176 { CCI_REG8(0x012a), 0x08 },
177 { CCI_REG8(0x012b), 0x08 },
179 { CCI_REG8(0x0a73), 0x60 },
180 { CCI_REG8(0x0a70), 0x11 },
181 { CCI_REG8(0x0313), 0x80 },
182 { CCI_REG8(0x0aff), 0x00 },
183 { CCI_REG8(0x0a70), 0x00 },
184 { CCI_REG8(0x00a4), 0x80 },
185 { CCI_REG8(0x0316), 0x01 },
186 { CCI_REG8(0x0a67), 0x00 },
187 { CCI_REG8(0x0084), 0x10 },
188 { CCI_REG8(0x0102), 0x09 },
191 static const struct cci_reg_sequence mode_1920x1080
[] = {
193 { CCI_REG8(0x0336), 0x45 },
194 { CCI_REG8(0x0383), 0x8b },
195 { CCI_REG8(0x0344), 0x02 },
196 { CCI_REG8(0x0345), 0xa6 },
197 { CCI_REG8(0x0346), 0x02 },
198 { CCI_REG8(0x0347), 0xb0 },
199 { CCI_REG8(0x0348), 0x07 },
200 { CCI_REG8(0x0349), 0x90 },
201 { CCI_REG8(0x034a), 0x04 },
202 { CCI_REG8(0x034b), 0x44 },
203 { CCI_REG8(0x0202), 0x03 },
204 { CCI_REG8(0x0203), 0x00 },
205 { CCI_REG8(0x0340), 0x04 },
206 { CCI_REG8(0x0341), 0xfc },
207 { CCI_REG8(0x0342), 0x07 },
208 { CCI_REG8(0x0343), 0x1c },
209 { CCI_REG8(0x0226), 0x00 },
210 { CCI_REG8(0x0227), 0x88 },
211 { CCI_REG8(0x0e38), 0x49 },
212 { CCI_REG8(0x0210), 0x13 },
213 { CCI_REG8(0x0218), 0x00 },
214 { CCI_REG8(0x0241), 0x88 },
215 { CCI_REG8(0x0392), 0x60 },
218 { CCI_REG8(0x00a2), 0xac },
219 { CCI_REG8(0x00a3), 0x02 },
220 { CCI_REG8(0x00ab), 0xa0 },
221 { CCI_REG8(0x00ac), 0x02 },
224 { CCI_REG8(0x0204), 0x04 },
225 { CCI_REG8(0x0205), 0x00 },
226 { CCI_REG8(0x0050), 0x38 },
227 { CCI_REG8(0x0051), 0x20 },
230 { CCI_REG8(0x009a), 0x66 },
231 { CCI_REG8(0x0351), 0x00 },
232 { CCI_REG8(0x0352), 0x06 },
233 { CCI_REG8(0x0353), 0x00 },
234 { CCI_REG8(0x0354), 0x08 },
235 { CCI_REG8(0x034c), 0x07 },
236 { CCI_REG8(0x034d), 0x80 },
237 { CCI_REG8(0x034e), 0x04 },
238 { CCI_REG8(0x034f), 0x38 },
241 { CCI_REG8(0x0114), 0x03 },
242 { CCI_REG8(0x0180), 0x65 },
243 { CCI_REG8(0x0181), 0xf0 },
244 { CCI_REG8(0x0185), 0x01 },
245 { CCI_REG8(0x0115), 0x30 },
246 { CCI_REG8(0x011b), 0x12 },
247 { CCI_REG8(0x011c), 0x12 },
248 { CCI_REG8(0x0121), 0x02 },
249 { CCI_REG8(0x0122), 0x03 },
250 { CCI_REG8(0x0123), 0x0c },
251 { CCI_REG8(0x0124), 0x00 },
252 { CCI_REG8(0x0125), 0x09 },
253 { CCI_REG8(0x0126), 0x06 },
254 { CCI_REG8(0x0129), 0x04 },
255 { CCI_REG8(0x012a), 0x03 },
256 { CCI_REG8(0x012b), 0x06 },
258 { CCI_REG8(0x0a73), 0x60 },
259 { CCI_REG8(0x0a70), 0x11 },
260 { CCI_REG8(0x0313), 0x80 },
261 { CCI_REG8(0x0aff), 0x00 },
262 { CCI_REG8(0x0a70), 0x00 },
263 { CCI_REG8(0x00a4), 0x80 },
264 { CCI_REG8(0x0316), 0x01 },
265 { CCI_REG8(0x0a67), 0x00 },
266 { CCI_REG8(0x0084), 0x10 },
267 { CCI_REG8(0x0102), 0x09 },
270 static const struct cci_reg_sequence mode_table_common
[] = {
271 { GC08A3_STREAMING_REG
, 0x00 },
273 { CCI_REG8(0x031c), 0x60 },
274 { CCI_REG8(0x0337), 0x04 },
275 { CCI_REG8(0x0335), 0x51 },
276 { CCI_REG8(0x0336), 0x70 },
277 { CCI_REG8(0x0383), 0xbb },
278 { CCI_REG8(0x031a), 0x00 },
279 { CCI_REG8(0x0321), 0x10 },
280 { CCI_REG8(0x0327), 0x03 },
281 { CCI_REG8(0x0325), 0x40 },
282 { CCI_REG8(0x0326), 0x23 },
283 { CCI_REG8(0x0314), 0x11 },
284 { CCI_REG8(0x0315), 0xd6 },
285 { CCI_REG8(0x0316), 0x01 },
286 { CCI_REG8(0x0334), 0x40 },
287 { CCI_REG8(0x0324), 0x42 },
288 { CCI_REG8(0x031c), 0x00 },
289 { CCI_REG8(0x031c), 0x9f },
290 { CCI_REG8(0x039a), 0x13 },
291 { CCI_REG8(0x0084), 0x30 },
292 { CCI_REG8(0x02b3), 0x08 },
293 { CCI_REG8(0x0057), 0x0c },
294 { CCI_REG8(0x05c3), 0x50 },
295 { CCI_REG8(0x0311), 0x90 },
296 { CCI_REG8(0x05a0), 0x02 },
297 { CCI_REG8(0x0074), 0x0a },
298 { CCI_REG8(0x0059), 0x11 },
299 { CCI_REG8(0x0070), 0x05 },
300 { CCI_REG8(0x0101), 0x00 },
303 { CCI_REG8(0x0344), 0x00 },
304 { CCI_REG8(0x0345), 0x06 },
305 { CCI_REG8(0x0346), 0x00 },
306 { CCI_REG8(0x0347), 0x04 },
307 { CCI_REG8(0x0348), 0x0c },
308 { CCI_REG8(0x0349), 0xd0 },
309 { CCI_REG8(0x034a), 0x09 },
310 { CCI_REG8(0x034b), 0x9c },
311 { CCI_REG8(0x0202), 0x09 },
312 { CCI_REG8(0x0203), 0x04 },
314 { CCI_REG8(0x0219), 0x05 },
315 { CCI_REG8(0x0226), 0x00 },
316 { CCI_REG8(0x0227), 0x28 },
317 { CCI_REG8(0x0e0a), 0x00 },
318 { CCI_REG8(0x0e0b), 0x00 },
319 { CCI_REG8(0x0e24), 0x04 },
320 { CCI_REG8(0x0e25), 0x04 },
321 { CCI_REG8(0x0e26), 0x00 },
322 { CCI_REG8(0x0e27), 0x10 },
323 { CCI_REG8(0x0e01), 0x74 },
324 { CCI_REG8(0x0e03), 0x47 },
325 { CCI_REG8(0x0e04), 0x33 },
326 { CCI_REG8(0x0e05), 0x44 },
327 { CCI_REG8(0x0e06), 0x44 },
328 { CCI_REG8(0x0e0c), 0x1e },
329 { CCI_REG8(0x0e17), 0x3a },
330 { CCI_REG8(0x0e18), 0x3c },
331 { CCI_REG8(0x0e19), 0x40 },
332 { CCI_REG8(0x0e1a), 0x42 },
333 { CCI_REG8(0x0e28), 0x21 },
334 { CCI_REG8(0x0e2b), 0x68 },
335 { CCI_REG8(0x0e2c), 0x0d },
336 { CCI_REG8(0x0e2d), 0x08 },
337 { CCI_REG8(0x0e34), 0xf4 },
338 { CCI_REG8(0x0e35), 0x44 },
339 { CCI_REG8(0x0e36), 0x07 },
340 { CCI_REG8(0x0e38), 0x49 },
341 { CCI_REG8(0x0210), 0x13 },
342 { CCI_REG8(0x0218), 0x00 },
343 { CCI_REG8(0x0241), 0x88 },
344 { CCI_REG8(0x0e32), 0x00 },
345 { CCI_REG8(0x0e33), 0x18 },
346 { CCI_REG8(0x0e42), 0x03 },
347 { CCI_REG8(0x0e43), 0x80 },
348 { CCI_REG8(0x0e44), 0x04 },
349 { CCI_REG8(0x0e45), 0x00 },
350 { CCI_REG8(0x0e4f), 0x04 },
351 { CCI_REG8(0x057a), 0x20 },
352 { CCI_REG8(0x0381), 0x7c },
353 { CCI_REG8(0x0382), 0x9b },
354 { CCI_REG8(0x0384), 0xfb },
355 { CCI_REG8(0x0389), 0x38 },
356 { CCI_REG8(0x038a), 0x03 },
357 { CCI_REG8(0x0390), 0x6a },
358 { CCI_REG8(0x0391), 0x0b },
359 { CCI_REG8(0x0392), 0x60 },
360 { CCI_REG8(0x0393), 0xc1 },
361 { CCI_REG8(0x0396), 0xff },
362 { CCI_REG8(0x0398), 0x62 },
365 { CCI_REG8(0x031c), 0x80 },
366 { CCI_REG8(0x03fe), 0x10 },
367 { CCI_REG8(0x03fe), 0x00 },
368 { CCI_REG8(0x031c), 0x9f },
369 { CCI_REG8(0x03fe), 0x00 },
370 { CCI_REG8(0x03fe), 0x00 },
371 { CCI_REG8(0x03fe), 0x00 },
372 { CCI_REG8(0x03fe), 0x00 },
373 { CCI_REG8(0x031c), 0x80 },
374 { CCI_REG8(0x03fe), 0x10 },
375 { CCI_REG8(0x03fe), 0x00 },
376 { CCI_REG8(0x031c), 0x9f },
377 { CCI_REG8(0x0360), 0x01 },
378 { CCI_REG8(0x0360), 0x00 },
379 { CCI_REG8(0x0316), 0x09 },
380 { CCI_REG8(0x0a67), 0x80 },
381 { CCI_REG8(0x0313), 0x00 },
382 { CCI_REG8(0x0a53), 0x0e },
383 { CCI_REG8(0x0a65), 0x17 },
384 { CCI_REG8(0x0a68), 0xa1 },
385 { CCI_REG8(0x0a58), 0x00 },
386 { CCI_REG8(0x0ace), 0x0c },
387 { CCI_REG8(0x00a4), 0x00 },
388 { CCI_REG8(0x00a5), 0x01 },
389 { CCI_REG8(0x00a7), 0x09 },
390 { CCI_REG8(0x00a8), 0x9c },
391 { CCI_REG8(0x00a9), 0x0c },
392 { CCI_REG8(0x00aa), 0xd0 },
393 { CCI_REG8(0x0a8a), 0x00 },
394 { CCI_REG8(0x0a8b), 0xe0 },
395 { CCI_REG8(0x0a8c), 0x13 },
396 { CCI_REG8(0x0a8d), 0xe8 },
397 { CCI_REG8(0x0a90), 0x0a },
398 { CCI_REG8(0x0a91), 0x10 },
399 { CCI_REG8(0x0a92), 0xf8 },
400 { CCI_REG8(0x0a71), 0xf2 },
401 { CCI_REG8(0x0a72), 0x12 },
402 { CCI_REG8(0x0a73), 0x64 },
403 { CCI_REG8(0x0a75), 0x41 },
404 { CCI_REG8(0x0a70), 0x07 },
405 { CCI_REG8(0x0313), 0x80 },
408 { CCI_REG8(0x00a0), 0x01 },
409 { CCI_REG8(0x0080), 0xd2 },
410 { CCI_REG8(0x0081), 0x3f },
411 { CCI_REG8(0x0087), 0x51 },
412 { CCI_REG8(0x0089), 0x03 },
413 { CCI_REG8(0x009b), 0x40 },
414 { CCI_REG8(0x05a0), 0x82 },
415 { CCI_REG8(0x05ac), 0x00 },
416 { CCI_REG8(0x05ad), 0x01 },
417 { CCI_REG8(0x05ae), 0x00 },
418 { CCI_REG8(0x0800), 0x0a },
419 { CCI_REG8(0x0801), 0x14 },
420 { CCI_REG8(0x0802), 0x28 },
421 { CCI_REG8(0x0803), 0x34 },
422 { CCI_REG8(0x0804), 0x0e },
423 { CCI_REG8(0x0805), 0x33 },
424 { CCI_REG8(0x0806), 0x03 },
425 { CCI_REG8(0x0807), 0x8a },
426 { CCI_REG8(0x0808), 0x50 },
427 { CCI_REG8(0x0809), 0x00 },
428 { CCI_REG8(0x080a), 0x34 },
429 { CCI_REG8(0x080b), 0x03 },
430 { CCI_REG8(0x080c), 0x26 },
431 { CCI_REG8(0x080d), 0x03 },
432 { CCI_REG8(0x080e), 0x18 },
433 { CCI_REG8(0x080f), 0x03 },
434 { CCI_REG8(0x0810), 0x10 },
435 { CCI_REG8(0x0811), 0x03 },
436 { CCI_REG8(0x0812), 0x00 },
437 { CCI_REG8(0x0813), 0x00 },
438 { CCI_REG8(0x0814), 0x01 },
439 { CCI_REG8(0x0815), 0x00 },
440 { CCI_REG8(0x0816), 0x01 },
441 { CCI_REG8(0x0817), 0x00 },
442 { CCI_REG8(0x0818), 0x00 },
443 { CCI_REG8(0x0819), 0x0a },
444 { CCI_REG8(0x081a), 0x01 },
445 { CCI_REG8(0x081b), 0x6c },
446 { CCI_REG8(0x081c), 0x00 },
447 { CCI_REG8(0x081d), 0x0b },
448 { CCI_REG8(0x081e), 0x02 },
449 { CCI_REG8(0x081f), 0x00 },
450 { CCI_REG8(0x0820), 0x00 },
451 { CCI_REG8(0x0821), 0x0c },
452 { CCI_REG8(0x0822), 0x02 },
453 { CCI_REG8(0x0823), 0xd9 },
454 { CCI_REG8(0x0824), 0x00 },
455 { CCI_REG8(0x0825), 0x0d },
456 { CCI_REG8(0x0826), 0x03 },
457 { CCI_REG8(0x0827), 0xf0 },
458 { CCI_REG8(0x0828), 0x00 },
459 { CCI_REG8(0x0829), 0x0e },
460 { CCI_REG8(0x082a), 0x05 },
461 { CCI_REG8(0x082b), 0x94 },
462 { CCI_REG8(0x082c), 0x09 },
463 { CCI_REG8(0x082d), 0x6e },
464 { CCI_REG8(0x082e), 0x07 },
465 { CCI_REG8(0x082f), 0xe6 },
466 { CCI_REG8(0x0830), 0x10 },
467 { CCI_REG8(0x0831), 0x0e },
468 { CCI_REG8(0x0832), 0x0b },
469 { CCI_REG8(0x0833), 0x2c },
470 { CCI_REG8(0x0834), 0x14 },
471 { CCI_REG8(0x0835), 0xae },
472 { CCI_REG8(0x0836), 0x0f },
473 { CCI_REG8(0x0837), 0xc4 },
474 { CCI_REG8(0x0838), 0x18 },
475 { CCI_REG8(0x0839), 0x0e },
476 { CCI_REG8(0x05ac), 0x01 },
477 { CCI_REG8(0x059a), 0x00 },
478 { CCI_REG8(0x059b), 0x00 },
479 { CCI_REG8(0x059c), 0x01 },
480 { CCI_REG8(0x0598), 0x00 },
481 { CCI_REG8(0x0597), 0x14 },
482 { CCI_REG8(0x05ab), 0x09 },
483 { CCI_REG8(0x05a4), 0x02 },
484 { CCI_REG8(0x05a3), 0x05 },
485 { CCI_REG8(0x05a0), 0xc2 },
486 { CCI_REG8(0x0207), 0xc4 },
489 { CCI_REG8(0x0208), 0x01 },
490 { CCI_REG8(0x0209), 0x72 },
491 { CCI_REG8(0x0204), 0x04 },
492 { CCI_REG8(0x0205), 0x00 },
494 { CCI_REG8(0x0040), 0x22 },
495 { CCI_REG8(0x0041), 0x20 },
496 { CCI_REG8(0x0043), 0x10 },
497 { CCI_REG8(0x0044), 0x00 },
498 { CCI_REG8(0x0046), 0x08 },
499 { CCI_REG8(0x0047), 0xf0 },
500 { CCI_REG8(0x0048), 0x0f },
501 { CCI_REG8(0x004b), 0x0f },
502 { CCI_REG8(0x004c), 0x00 },
503 { CCI_REG8(0x0050), 0x5c },
504 { CCI_REG8(0x0051), 0x44 },
505 { CCI_REG8(0x005b), 0x03 },
506 { CCI_REG8(0x00c0), 0x00 },
507 { CCI_REG8(0x00c1), 0x80 },
508 { CCI_REG8(0x00c2), 0x31 },
509 { CCI_REG8(0x00c3), 0x00 },
510 { CCI_REG8(0x0460), 0x04 },
511 { CCI_REG8(0x0462), 0x08 },
512 { CCI_REG8(0x0464), 0x0e },
513 { CCI_REG8(0x0466), 0x0a },
514 { CCI_REG8(0x0468), 0x12 },
515 { CCI_REG8(0x046a), 0x12 },
516 { CCI_REG8(0x046c), 0x10 },
517 { CCI_REG8(0x046e), 0x0c },
518 { CCI_REG8(0x0461), 0x03 },
519 { CCI_REG8(0x0463), 0x03 },
520 { CCI_REG8(0x0465), 0x03 },
521 { CCI_REG8(0x0467), 0x03 },
522 { CCI_REG8(0x0469), 0x04 },
523 { CCI_REG8(0x046b), 0x04 },
524 { CCI_REG8(0x046d), 0x04 },
525 { CCI_REG8(0x046f), 0x04 },
526 { CCI_REG8(0x0470), 0x04 },
527 { CCI_REG8(0x0472), 0x10 },
528 { CCI_REG8(0x0474), 0x26 },
529 { CCI_REG8(0x0476), 0x38 },
530 { CCI_REG8(0x0478), 0x20 },
531 { CCI_REG8(0x047a), 0x30 },
532 { CCI_REG8(0x047c), 0x38 },
533 { CCI_REG8(0x047e), 0x60 },
534 { CCI_REG8(0x0471), 0x05 },
535 { CCI_REG8(0x0473), 0x05 },
536 { CCI_REG8(0x0475), 0x05 },
537 { CCI_REG8(0x0477), 0x05 },
538 { CCI_REG8(0x0479), 0x04 },
539 { CCI_REG8(0x047b), 0x04 },
540 { CCI_REG8(0x047d), 0x04 },
541 { CCI_REG8(0x047f), 0x04 },
547 const struct gc08a3_reg_list reg_list
;
549 u32 hts
; /* Horizontal timining size */
550 u32 vts_def
; /* Default vertical timining size */
551 u32 vts_min
; /* Min vertical timining size */
554 /* Declare modes in order, from biggest to smallest height. */
555 static const struct gc08a3_mode gc08a3_modes
[] = {
557 /* 3264*2448@30fps */
558 .width
= GC08A3_NATIVE_WIDTH
,
559 .height
= GC08A3_NATIVE_HEIGHT
,
561 .num_of_regs
= ARRAY_SIZE(mode_3264x2448
),
562 .regs
= mode_3264x2448
,
569 /* 1920*1080@60fps */
573 .num_of_regs
= ARRAY_SIZE(mode_1920x1080
),
574 .regs
= mode_1920x1080
,
582 static inline struct gc08a3
*to_gc08a3(struct v4l2_subdev
*sd
)
584 return container_of(sd
, struct gc08a3
, sd
);
587 static int gc08a3_power_on(struct device
*dev
)
589 struct v4l2_subdev
*sd
= dev_get_drvdata(dev
);
590 struct gc08a3
*gc08a3
= to_gc08a3(sd
);
593 ret
= regulator_bulk_enable(ARRAY_SIZE(gc08a3_supply_name
),
596 dev_err(gc08a3
->dev
, "failed to enable regulators: %d\n", ret
);
600 ret
= clk_prepare_enable(gc08a3
->xclk
);
602 regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name
),
604 dev_err(gc08a3
->dev
, "clk prepare enable failed\n");
608 fsleep(GC08A3_SLEEP_US
);
610 gpiod_set_value_cansleep(gc08a3
->reset_gpio
, 0);
611 fsleep(GC08A3_SLEEP_US
);
616 static int gc08a3_power_off(struct device
*dev
)
618 struct v4l2_subdev
*sd
= dev_get_drvdata(dev
);
619 struct gc08a3
*gc08a3
= to_gc08a3(sd
);
621 clk_disable_unprepare(gc08a3
->xclk
);
622 gpiod_set_value_cansleep(gc08a3
->reset_gpio
, 1);
623 regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name
),
629 static int gc08a3_enum_mbus_code(struct v4l2_subdev
*sd
,
630 struct v4l2_subdev_state
*sd_state
,
631 struct v4l2_subdev_mbus_code_enum
*code
)
636 code
->code
= GC08A3_MBUS_CODE
;
641 static int gc08a3_enum_frame_size(struct v4l2_subdev
*subdev
,
642 struct v4l2_subdev_state
*sd_state
,
643 struct v4l2_subdev_frame_size_enum
*fse
)
645 if (fse
->code
!= GC08A3_MBUS_CODE
)
648 if (fse
->index
>= ARRAY_SIZE(gc08a3_modes
))
651 fse
->min_width
= gc08a3_modes
[fse
->index
].width
;
652 fse
->max_width
= gc08a3_modes
[fse
->index
].width
;
653 fse
->min_height
= gc08a3_modes
[fse
->index
].height
;
654 fse
->max_height
= gc08a3_modes
[fse
->index
].height
;
659 static int gc08a3_update_cur_mode_controls(struct gc08a3
*gc08a3
,
660 const struct gc08a3_mode
*mode
)
662 s64 exposure_max
, h_blank
;
665 ret
= __v4l2_ctrl_modify_range(gc08a3
->vblank
,
666 mode
->vts_min
- mode
->height
,
667 GC08A3_VTS_MAX
- mode
->height
, 1,
668 mode
->vts_def
- mode
->height
);
670 dev_err(gc08a3
->dev
, "VB ctrl range update failed\n");
674 h_blank
= mode
->hts
- mode
->width
;
675 ret
= __v4l2_ctrl_modify_range(gc08a3
->hblank
, h_blank
, h_blank
, 1,
678 dev_err(gc08a3
->dev
, "HB ctrl range update failed\n");
682 exposure_max
= mode
->vts_def
- GC08A3_EXP_MARGIN
;
683 ret
= __v4l2_ctrl_modify_range(gc08a3
->exposure
, GC08A3_EXP_MIN
,
684 exposure_max
, GC08A3_EXP_STEP
,
687 dev_err(gc08a3
->dev
, "exposure ctrl range update failed\n");
694 static void gc08a3_update_pad_format(struct gc08a3
*gc08a3
,
695 const struct gc08a3_mode
*mode
,
696 struct v4l2_mbus_framefmt
*fmt
)
698 fmt
->width
= mode
->width
;
699 fmt
->height
= mode
->height
;
700 fmt
->code
= GC08A3_MBUS_CODE
;
701 fmt
->field
= V4L2_FIELD_NONE
;
702 fmt
->colorspace
= V4L2_COLORSPACE_RAW
;
703 fmt
->ycbcr_enc
= V4L2_MAP_YCBCR_ENC_DEFAULT(fmt
->colorspace
);
704 fmt
->quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
705 fmt
->xfer_func
= V4L2_XFER_FUNC_NONE
;
708 static int gc08a3_set_format(struct v4l2_subdev
*sd
,
709 struct v4l2_subdev_state
*state
,
710 struct v4l2_subdev_format
*fmt
)
712 struct gc08a3
*gc08a3
= to_gc08a3(sd
);
713 struct v4l2_mbus_framefmt
*mbus_fmt
;
714 struct v4l2_rect
*crop
;
715 const struct gc08a3_mode
*mode
;
717 mode
= v4l2_find_nearest_size(gc08a3_modes
, ARRAY_SIZE(gc08a3_modes
),
718 width
, height
, fmt
->format
.width
,
721 /* update crop info to subdev state */
722 crop
= v4l2_subdev_state_get_crop(state
, 0);
723 crop
->width
= mode
->width
;
724 crop
->height
= mode
->height
;
726 /* update fmt info to subdev state */
727 gc08a3_update_pad_format(gc08a3
, mode
, &fmt
->format
);
728 mbus_fmt
= v4l2_subdev_state_get_format(state
, 0);
729 *mbus_fmt
= fmt
->format
;
731 if (fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
)
734 gc08a3
->cur_mode
= mode
;
735 gc08a3_update_cur_mode_controls(gc08a3
, mode
);
740 static int gc08a3_get_selection(struct v4l2_subdev
*sd
,
741 struct v4l2_subdev_state
*state
,
742 struct v4l2_subdev_selection
*sel
)
744 switch (sel
->target
) {
745 case V4L2_SEL_TGT_CROP_DEFAULT
:
746 case V4L2_SEL_TGT_CROP
:
747 sel
->r
= *v4l2_subdev_state_get_crop(state
, 0);
749 case V4L2_SEL_TGT_CROP_BOUNDS
:
752 sel
->r
.width
= GC08A3_NATIVE_WIDTH
;
753 sel
->r
.height
= GC08A3_NATIVE_HEIGHT
;
762 static int gc08a3_init_state(struct v4l2_subdev
*sd
,
763 struct v4l2_subdev_state
*state
)
765 struct v4l2_subdev_format fmt
= {
766 .which
= V4L2_SUBDEV_FORMAT_TRY
,
769 .code
= GC08A3_MBUS_CODE
,
770 .width
= gc08a3_modes
[0].width
,
771 .height
= gc08a3_modes
[0].height
,
775 gc08a3_set_format(sd
, state
, &fmt
);
780 static int gc08a3_set_ctrl_hflip(struct gc08a3
*gc08a3
, u32 ctrl_val
)
785 ret
= cci_read(gc08a3
->regmap
, GC08A3_FLIP_REG
, &val
, NULL
);
787 dev_err(gc08a3
->dev
, "read hflip register failed: %d\n", ret
);
791 return cci_update_bits(gc08a3
->regmap
, GC08A3_FLIP_REG
,
793 ctrl_val
? GC08A3_FLIP_H_MASK
: 0, NULL
);
796 static int gc08a3_set_ctrl_vflip(struct gc08a3
*gc08a3
, u32 ctrl_val
)
801 ret
= cci_read(gc08a3
->regmap
, GC08A3_FLIP_REG
, &val
, NULL
);
803 dev_err(gc08a3
->dev
, "read vflip register failed: %d\n", ret
);
807 return cci_update_bits(gc08a3
->regmap
, GC08A3_FLIP_REG
,
809 ctrl_val
? GC08A3_FLIP_V_MASK
: 0, NULL
);
812 static int gc08a3_test_pattern(struct gc08a3
*gc08a3
, u32 pattern_menu
)
818 switch (pattern_menu
) {
830 pattern
= pattern_menu
+ 1;
837 ret
= cci_write(gc08a3
->regmap
, GC08A3_REG_TEST_PATTERN_IDX
,
842 return cci_write(gc08a3
->regmap
, GC08A3_REG_TEST_PATTERN_EN
,
843 GC08A3_TEST_PATTERN_EN
, NULL
);
845 return cci_write(gc08a3
->regmap
, GC08A3_REG_TEST_PATTERN_EN
,
850 static int gc08a3_set_ctrl(struct v4l2_ctrl
*ctrl
)
852 struct gc08a3
*gc08a3
=
853 container_of(ctrl
->handler
, struct gc08a3
, ctrls
);
856 struct v4l2_subdev_state
*state
;
857 const struct v4l2_mbus_framefmt
*format
;
859 state
= v4l2_subdev_get_locked_active_state(&gc08a3
->sd
);
860 format
= v4l2_subdev_state_get_format(state
, 0);
862 if (ctrl
->id
== V4L2_CID_VBLANK
) {
863 /* Update max exposure while meeting expected vblanking */
864 exposure_max
= format
->height
+ ctrl
->val
- GC08A3_EXP_MARGIN
;
865 __v4l2_ctrl_modify_range(gc08a3
->exposure
,
866 gc08a3
->exposure
->minimum
,
867 exposure_max
, gc08a3
->exposure
->step
,
872 * Applying V4L2 control value only happens
873 * when power is on for streaming.
875 if (!pm_runtime_get_if_active(gc08a3
->dev
))
879 case V4L2_CID_EXPOSURE
:
880 ret
= cci_write(gc08a3
->regmap
, GC08A3_EXP_REG
,
884 case V4L2_CID_ANALOGUE_GAIN
:
885 ret
= cci_write(gc08a3
->regmap
, GC08A3_AGAIN_REG
,
889 case V4L2_CID_VBLANK
:
890 ret
= cci_write(gc08a3
->regmap
, GC08A3_FRAME_LENGTH_REG
,
891 gc08a3
->cur_mode
->height
+ ctrl
->val
, NULL
);
895 ret
= gc08a3_set_ctrl_hflip(gc08a3
, ctrl
->val
);
899 ret
= gc08a3_set_ctrl_vflip(gc08a3
, ctrl
->val
);
902 case V4L2_CID_TEST_PATTERN
:
903 ret
= gc08a3_test_pattern(gc08a3
, ctrl
->val
);
910 pm_runtime_put(gc08a3
->dev
);
915 static const struct v4l2_ctrl_ops gc08a3_ctrl_ops
= {
916 .s_ctrl
= gc08a3_set_ctrl
,
919 static int gc08a3_start_streaming(struct gc08a3
*gc08a3
)
921 const struct gc08a3_mode
*mode
;
922 const struct gc08a3_reg_list
*reg_list
;
925 ret
= pm_runtime_resume_and_get(gc08a3
->dev
);
929 ret
= cci_multi_reg_write(gc08a3
->regmap
,
931 ARRAY_SIZE(mode_table_common
), NULL
);
935 mode
= gc08a3
->cur_mode
;
936 reg_list
= &mode
->reg_list
;
937 ret
= cci_multi_reg_write(gc08a3
->regmap
,
938 reg_list
->regs
, reg_list
->num_of_regs
, NULL
);
942 ret
= __v4l2_ctrl_handler_setup(&gc08a3
->ctrls
);
944 dev_err(gc08a3
->dev
, "could not sync v4l2 controls\n");
948 ret
= cci_write(gc08a3
->regmap
, GC08A3_STREAMING_REG
, 1, NULL
);
950 dev_err(gc08a3
->dev
, "write STREAMING_REG failed: %d\n", ret
);
957 pm_runtime_put(gc08a3
->dev
);
961 static int gc08a3_stop_streaming(struct gc08a3
*gc08a3
)
965 ret
= cci_write(gc08a3
->regmap
, GC08A3_STREAMING_REG
, 0, NULL
);
967 dev_err(gc08a3
->dev
, "could not sent stop streaming %d\n", ret
);
969 pm_runtime_put(gc08a3
->dev
);
973 static int gc08a3_s_stream(struct v4l2_subdev
*subdev
, int enable
)
975 struct gc08a3
*gc08a3
= to_gc08a3(subdev
);
976 struct v4l2_subdev_state
*state
;
979 state
= v4l2_subdev_lock_and_get_active_state(subdev
);
982 ret
= gc08a3_start_streaming(gc08a3
);
984 ret
= gc08a3_stop_streaming(gc08a3
);
986 v4l2_subdev_unlock_state(state
);
991 static const struct v4l2_subdev_video_ops gc08a3_video_ops
= {
992 .s_stream
= gc08a3_s_stream
,
995 static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops
= {
996 .enum_mbus_code
= gc08a3_enum_mbus_code
,
997 .enum_frame_size
= gc08a3_enum_frame_size
,
998 .get_fmt
= v4l2_subdev_get_fmt
,
999 .set_fmt
= gc08a3_set_format
,
1000 .get_selection
= gc08a3_get_selection
,
1003 static const struct v4l2_subdev_ops gc08a3_subdev_ops
= {
1004 .video
= &gc08a3_video_ops
,
1005 .pad
= &gc08a3_subdev_pad_ops
,
1008 static const struct v4l2_subdev_internal_ops gc08a3_internal_ops
= {
1009 .init_state
= gc08a3_init_state
,
1012 static int gc08a3_get_regulators(struct device
*dev
, struct gc08a3
*gc08a3
)
1016 for (i
= 0; i
< ARRAY_SIZE(gc08a3_supply_name
); i
++)
1017 gc08a3
->supplies
[i
].supply
= gc08a3_supply_name
[i
];
1019 return devm_regulator_bulk_get(dev
, ARRAY_SIZE(gc08a3_supply_name
),
1023 static int gc08a3_parse_fwnode(struct gc08a3
*gc08a3
)
1025 struct fwnode_handle
*endpoint
;
1026 struct v4l2_fwnode_endpoint bus_cfg
= {
1027 .bus_type
= V4L2_MBUS_CSI2_DPHY
,
1030 struct device
*dev
= gc08a3
->dev
;
1033 fwnode_graph_get_endpoint_by_id(dev_fwnode(dev
), 0, 0,
1034 FWNODE_GRAPH_ENDPOINT_NEXT
);
1036 dev_err(dev
, "endpoint node not found\n");
1040 ret
= v4l2_fwnode_endpoint_alloc_parse(endpoint
, &bus_cfg
);
1042 dev_err(dev
, "parsing endpoint node failed\n");
1046 ret
= v4l2_link_freq_to_bitmap(dev
, bus_cfg
.link_frequencies
,
1047 bus_cfg
.nr_of_link_frequencies
,
1048 gc08a3_link_freq_menu_items
,
1049 ARRAY_SIZE(gc08a3_link_freq_menu_items
),
1050 &gc08a3
->link_freq_bitmap
);
1055 v4l2_fwnode_endpoint_free(&bus_cfg
);
1056 fwnode_handle_put(endpoint
);
1060 static u64
gc08a3_to_pixel_rate(u32 f_index
)
1063 gc08a3_link_freq_menu_items
[f_index
] * 2 * GC08A3_DATA_LANES
;
1065 return div_u64(pixel_rate
, GC08A3_RGB_DEPTH
);
1068 static int gc08a3_init_controls(struct gc08a3
*gc08a3
)
1070 struct i2c_client
*client
= v4l2_get_subdevdata(&gc08a3
->sd
);
1071 const struct gc08a3_mode
*mode
= &gc08a3_modes
[0];
1072 const struct v4l2_ctrl_ops
*ops
= &gc08a3_ctrl_ops
;
1073 struct v4l2_fwnode_device_properties props
;
1074 struct v4l2_ctrl_handler
*ctrl_hdlr
;
1075 s64 exposure_max
, h_blank
;
1078 ctrl_hdlr
= &gc08a3
->ctrls
;
1079 ret
= v4l2_ctrl_handler_init(ctrl_hdlr
, 9);
1083 gc08a3
->hflip
= v4l2_ctrl_new_std(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1084 V4L2_CID_HFLIP
, 0, 1, 1, 0);
1085 gc08a3
->vflip
= v4l2_ctrl_new_std(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1086 V4L2_CID_VFLIP
, 0, 1, 1, 0);
1087 v4l2_ctrl_cluster(2, &gc08a3
->hflip
);
1090 v4l2_ctrl_new_int_menu(ctrl_hdlr
,
1093 ARRAY_SIZE(gc08a3_link_freq_menu_items
) - 1,
1095 gc08a3_link_freq_menu_items
);
1096 if (gc08a3
->link_freq
)
1097 gc08a3
->link_freq
->flags
|= V4L2_CTRL_FLAG_READ_ONLY
;
1099 gc08a3
->pixel_rate
=
1100 v4l2_ctrl_new_std(ctrl_hdlr
,
1102 V4L2_CID_PIXEL_RATE
, 0,
1103 gc08a3_to_pixel_rate(0),
1105 gc08a3_to_pixel_rate(0));
1108 v4l2_ctrl_new_std(ctrl_hdlr
,
1109 &gc08a3_ctrl_ops
, V4L2_CID_VBLANK
,
1110 mode
->vts_min
- mode
->height
,
1111 GC08A3_VTS_MAX
- mode
->height
, 1,
1112 mode
->vts_def
- mode
->height
);
1114 h_blank
= mode
->hts
- mode
->width
;
1115 gc08a3
->hblank
= v4l2_ctrl_new_std(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1116 V4L2_CID_HBLANK
, h_blank
, h_blank
, 1,
1119 gc08a3
->hblank
->flags
|= V4L2_CTRL_FLAG_READ_ONLY
;
1121 v4l2_ctrl_new_std(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1122 V4L2_CID_ANALOGUE_GAIN
, GC08A3_AGAIN_MIN
,
1123 GC08A3_AGAIN_MAX
, GC08A3_AGAIN_STEP
,
1126 exposure_max
= mode
->vts_def
- GC08A3_EXP_MARGIN
;
1127 gc08a3
->exposure
= v4l2_ctrl_new_std(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1128 V4L2_CID_EXPOSURE
, GC08A3_EXP_MIN
,
1129 exposure_max
, GC08A3_EXP_STEP
,
1132 v4l2_ctrl_new_std_menu_items(ctrl_hdlr
, &gc08a3_ctrl_ops
,
1133 V4L2_CID_TEST_PATTERN
,
1134 ARRAY_SIZE(gc08a3_test_pattern_menu
) - 1,
1135 0, 0, gc08a3_test_pattern_menu
);
1137 /* register properties to fwnode (e.g. rotation, orientation) */
1138 ret
= v4l2_fwnode_device_parse(&client
->dev
, &props
);
1142 ret
= v4l2_ctrl_new_fwnode_properties(ctrl_hdlr
, ops
, &props
);
1146 if (ctrl_hdlr
->error
) {
1147 ret
= ctrl_hdlr
->error
;
1151 gc08a3
->sd
.ctrl_handler
= ctrl_hdlr
;
1156 v4l2_ctrl_handler_free(ctrl_hdlr
);
1161 static int gc08a3_identify_module(struct gc08a3
*gc08a3
)
1166 ret
= cci_read(gc08a3
->regmap
, GC08A3_REG_CHIP_ID
, &val
, NULL
);
1168 dev_err(gc08a3
->dev
, "failed to read chip id");
1172 if (val
!= GC08A3_CHIP_ID
) {
1173 dev_err(gc08a3
->dev
, "chip id mismatch: 0x%x!=0x%llx",
1174 GC08A3_CHIP_ID
, val
);
1181 static int gc08a3_probe(struct i2c_client
*client
)
1183 struct device
*dev
= &client
->dev
;
1184 struct gc08a3
*gc08a3
;
1187 gc08a3
= devm_kzalloc(dev
, sizeof(*gc08a3
), GFP_KERNEL
);
1193 ret
= gc08a3_parse_fwnode(gc08a3
);
1197 gc08a3
->regmap
= devm_cci_regmap_init_i2c(client
, 16);
1198 if (IS_ERR(gc08a3
->regmap
))
1199 return dev_err_probe(dev
, PTR_ERR(gc08a3
->regmap
),
1200 "failed to init CCI\n");
1202 gc08a3
->xclk
= devm_clk_get(dev
, NULL
);
1203 if (IS_ERR(gc08a3
->xclk
))
1204 return dev_err_probe(dev
, PTR_ERR(gc08a3
->xclk
),
1205 "failed to get xclk\n");
1207 ret
= clk_set_rate(gc08a3
->xclk
, GC08A3_DEFAULT_CLK_FREQ
);
1209 return dev_err_probe(dev
, ret
,
1210 "failed to set xclk frequency\n");
1212 ret
= gc08a3_get_regulators(dev
, gc08a3
);
1214 return dev_err_probe(dev
, ret
,
1215 "failed to get regulators\n");
1217 gc08a3
->reset_gpio
= devm_gpiod_get(dev
, "reset", GPIOD_OUT_LOW
);
1218 if (IS_ERR(gc08a3
->reset_gpio
))
1219 return dev_err_probe(dev
, PTR_ERR(gc08a3
->reset_gpio
),
1220 "failed to get gpio\n");
1222 v4l2_i2c_subdev_init(&gc08a3
->sd
, client
, &gc08a3_subdev_ops
);
1223 gc08a3
->sd
.internal_ops
= &gc08a3_internal_ops
;
1224 gc08a3
->cur_mode
= &gc08a3_modes
[0];
1226 ret
= gc08a3_power_on(gc08a3
->dev
);
1228 return dev_err_probe(dev
, ret
,
1229 "failed to sensor power on\n");
1231 ret
= gc08a3_identify_module(gc08a3
);
1233 dev_err(&client
->dev
, "failed to find sensor: %d\n", ret
);
1237 ret
= gc08a3_init_controls(gc08a3
);
1239 dev_err(&client
->dev
, "failed to init controls: %d", ret
);
1243 gc08a3
->sd
.flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
1244 gc08a3
->pad
.flags
= MEDIA_PAD_FL_SOURCE
;
1245 gc08a3
->sd
.dev
= &client
->dev
;
1246 gc08a3
->sd
.entity
.function
= MEDIA_ENT_F_CAM_SENSOR
;
1248 ret
= media_entity_pads_init(&gc08a3
->sd
.entity
, 1, &gc08a3
->pad
);
1250 dev_err(dev
, "could not register media entity\n");
1251 goto err_v4l2_ctrl_handler_free
;
1254 gc08a3
->sd
.state_lock
= gc08a3
->ctrls
.lock
;
1255 ret
= v4l2_subdev_init_finalize(&gc08a3
->sd
);
1257 dev_err(dev
, "v4l2 subdev init error: %d\n", ret
);
1258 goto err_media_entity_cleanup
;
1261 pm_runtime_set_active(gc08a3
->dev
);
1262 pm_runtime_enable(gc08a3
->dev
);
1263 pm_runtime_set_autosuspend_delay(gc08a3
->dev
, 1000);
1264 pm_runtime_use_autosuspend(gc08a3
->dev
);
1265 pm_runtime_idle(gc08a3
->dev
);
1267 ret
= v4l2_async_register_subdev_sensor(&gc08a3
->sd
);
1269 dev_err(dev
, "could not register v4l2 device\n");
1276 pm_runtime_disable(gc08a3
->dev
);
1277 v4l2_subdev_cleanup(&gc08a3
->sd
);
1279 err_media_entity_cleanup
:
1280 media_entity_cleanup(&gc08a3
->sd
.entity
);
1282 err_v4l2_ctrl_handler_free
:
1283 v4l2_ctrl_handler_free(gc08a3
->sd
.ctrl_handler
);
1286 gc08a3_power_off(gc08a3
->dev
);
1291 static void gc08a3_remove(struct i2c_client
*client
)
1293 struct v4l2_subdev
*sd
= i2c_get_clientdata(client
);
1294 struct gc08a3
*gc08a3
= to_gc08a3(sd
);
1296 v4l2_async_unregister_subdev(&gc08a3
->sd
);
1297 v4l2_subdev_cleanup(sd
);
1298 media_entity_cleanup(&gc08a3
->sd
.entity
);
1299 v4l2_ctrl_handler_free(&gc08a3
->ctrls
);
1301 pm_runtime_disable(&client
->dev
);
1302 if (!pm_runtime_status_suspended(&client
->dev
))
1303 gc08a3_power_off(gc08a3
->dev
);
1304 pm_runtime_set_suspended(&client
->dev
);
1307 static const struct of_device_id gc08a3_of_match
[] = {
1308 { .compatible
= "galaxycore,gc08a3" },
1311 MODULE_DEVICE_TABLE(of
, gc08a3_of_match
);
1313 static DEFINE_RUNTIME_DEV_PM_OPS(gc08a3_pm_ops
,
1318 static struct i2c_driver gc08a3_i2c_driver
= {
1320 .of_match_table
= gc08a3_of_match
,
1321 .pm
= pm_ptr(&gc08a3_pm_ops
),
1324 .probe
= gc08a3_probe
,
1325 .remove
= gc08a3_remove
,
1327 module_i2c_driver(gc08a3_i2c_driver
);
1329 MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver");
1330 MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>");
1331 MODULE_LICENSE("GPL");