1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2017 Microchip Corporation. |
3 | |
4 | #include <linux/clk.h> |
5 | #include <linux/delay.h> |
6 | #include <linux/gpio/consumer.h> |
7 | #include <linux/i2c.h> |
8 | #include <linux/module.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <linux/regmap.h> |
11 | #include <media/v4l2-ctrls.h> |
12 | #include <media/v4l2-event.h> |
13 | #include <media/v4l2-image-sizes.h> |
14 | #include <media/v4l2-subdev.h> |
15 | |
16 | #define REG_OUTSIZE_LSB 0x34 |
17 | |
18 | /* OV7740 register tables */ |
19 | #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ |
20 | #define REG_BGAIN 0x01 /* blue gain */ |
21 | #define REG_RGAIN 0x02 /* red gain */ |
22 | #define REG_GGAIN 0x03 /* green gain */ |
23 | #define REG_REG04 0x04 /* analog setting, don't change*/ |
24 | #define REG_BAVG 0x05 /* b channel average */ |
25 | #define REG_GAVG 0x06 /* g channel average */ |
26 | #define REG_RAVG 0x07 /* r channel average */ |
27 | |
28 | #define REG_REG0C 0x0C /* filp enable */ |
29 | #define REG0C_IMG_FLIP 0x80 |
30 | #define REG0C_IMG_MIRROR 0x40 |
31 | |
32 | #define REG_REG0E 0x0E /* blc line */ |
33 | #define REG_HAEC 0x0F /* auto exposure cntrl */ |
34 | #define REG_AEC 0x10 /* auto exposure cntrl */ |
35 | |
36 | #define REG_CLK 0x11 /* Clock control */ |
37 | #define REG_REG55 0x55 /* Clock PLL DIV/PreDiv */ |
38 | |
39 | #define REG_REG12 0x12 |
40 | |
41 | #define REG_REG13 0x13 /* auto/manual AGC, AEC, Write Balance*/ |
42 | #define REG13_AEC_EN 0x01 |
43 | #define REG13_AGC_EN 0x04 |
44 | |
45 | #define REG_REG14 0x14 |
46 | #define REG_CTRL15 0x15 |
47 | #define REG15_GAIN_MSB 0x03 |
48 | |
49 | #define REG_REG16 0x16 |
50 | |
51 | #define REG_MIDH 0x1C /* manufacture id byte */ |
52 | #define REG_MIDL 0x1D /* manufacture id byre */ |
53 | #define REG_PIDH 0x0A /* Product ID MSB */ |
54 | #define REG_PIDL 0x0B /* Product ID LSB */ |
55 | |
56 | #define REG_84 0x84 /* lots of stuff */ |
57 | #define REG_REG38 0x38 /* sub-addr */ |
58 | |
59 | #define REG_AHSTART 0x17 /* Horiz start high bits */ |
60 | #define REG_AHSIZE 0x18 |
61 | #define REG_AVSTART 0x19 /* Vert start high bits */ |
62 | #define REG_AVSIZE 0x1A |
63 | #define REG_PSHFT 0x1b /* Pixel delay after HREF */ |
64 | |
65 | #define REG_HOUTSIZE 0x31 |
66 | #define REG_VOUTSIZE 0x32 |
67 | #define REG_HVSIZEOFF 0x33 |
68 | #define REG_REG34 0x34 /* DSP output size H/V LSB*/ |
69 | |
70 | #define REG_ISP_CTRL00 0x80 |
71 | #define ISPCTRL00_AWB_EN 0x10 |
72 | #define ISPCTRL00_AWB_GAIN_EN 0x04 |
73 | |
74 | #define REG_YGAIN 0xE2 /* ygain for contrast control */ |
75 | |
76 | #define REG_YBRIGHT 0xE3 |
77 | #define REG_SGNSET 0xE4 |
78 | #define SGNSET_YBRIGHT_MASK 0x08 |
79 | |
80 | #define REG_USAT 0xDD |
81 | #define REG_VSAT 0xDE |
82 | |
83 | |
84 | struct ov7740 { |
85 | struct v4l2_subdev subdev; |
86 | struct media_pad pad; |
87 | struct v4l2_mbus_framefmt format; |
88 | const struct ov7740_pixfmt *fmt; /* Current format */ |
89 | const struct ov7740_framesize *frmsize; |
90 | struct regmap *regmap; |
91 | struct clk *xvclk; |
92 | struct v4l2_ctrl_handler ctrl_handler; |
93 | struct { |
94 | /* gain cluster */ |
95 | struct v4l2_ctrl *auto_gain; |
96 | struct v4l2_ctrl *gain; |
97 | }; |
98 | struct { |
99 | struct v4l2_ctrl *auto_wb; |
100 | struct v4l2_ctrl *blue_balance; |
101 | struct v4l2_ctrl *red_balance; |
102 | }; |
103 | struct { |
104 | struct v4l2_ctrl *hflip; |
105 | struct v4l2_ctrl *vflip; |
106 | }; |
107 | struct { |
108 | /* exposure cluster */ |
109 | struct v4l2_ctrl *auto_exposure; |
110 | struct v4l2_ctrl *exposure; |
111 | }; |
112 | struct { |
113 | /* saturation/hue cluster */ |
114 | struct v4l2_ctrl *saturation; |
115 | struct v4l2_ctrl *hue; |
116 | }; |
117 | struct v4l2_ctrl *brightness; |
118 | struct v4l2_ctrl *contrast; |
119 | |
120 | struct mutex mutex; /* To serialize asynchronus callbacks */ |
121 | |
122 | struct gpio_desc *resetb_gpio; |
123 | struct gpio_desc *pwdn_gpio; |
124 | }; |
125 | |
126 | struct ov7740_pixfmt { |
127 | u32 mbus_code; |
128 | enum v4l2_colorspace colorspace; |
129 | const struct reg_sequence *regs; |
130 | u32 reg_num; |
131 | }; |
132 | |
133 | struct ov7740_framesize { |
134 | u16 width; |
135 | u16 height; |
136 | const struct reg_sequence *regs; |
137 | u32 reg_num; |
138 | }; |
139 | |
140 | static const struct reg_sequence ov7740_vga[] = { |
141 | {0x55, 0x40}, |
142 | {0x11, 0x02}, |
143 | |
144 | {0xd5, 0x10}, |
145 | {0x0c, 0x12}, |
146 | {0x0d, 0x34}, |
147 | {0x17, 0x25}, |
148 | {0x18, 0xa0}, |
149 | {0x19, 0x03}, |
150 | {0x1a, 0xf0}, |
151 | {0x1b, 0x89}, |
152 | {0x22, 0x03}, |
153 | {0x29, 0x18}, |
154 | {0x2b, 0xf8}, |
155 | {0x2c, 0x01}, |
156 | {REG_HOUTSIZE, 0xa0}, |
157 | {REG_VOUTSIZE, 0xf0}, |
158 | {0x33, 0xc4}, |
159 | {REG_OUTSIZE_LSB, 0x0}, |
160 | {0x35, 0x05}, |
161 | {0x04, 0x60}, |
162 | {0x27, 0x80}, |
163 | {0x3d, 0x0f}, |
164 | {0x3e, 0x80}, |
165 | {0x3f, 0x40}, |
166 | {0x40, 0x7f}, |
167 | {0x41, 0x6a}, |
168 | {0x42, 0x29}, |
169 | {0x44, 0x22}, |
170 | {0x45, 0x41}, |
171 | {0x47, 0x02}, |
172 | {0x49, 0x64}, |
173 | {0x4a, 0xa1}, |
174 | {0x4b, 0x40}, |
175 | {0x4c, 0x1a}, |
176 | {0x4d, 0x50}, |
177 | {0x4e, 0x13}, |
178 | {0x64, 0x00}, |
179 | {0x67, 0x88}, |
180 | {0x68, 0x1a}, |
181 | |
182 | {0x14, 0x28}, |
183 | {0x24, 0x3c}, |
184 | {0x25, 0x30}, |
185 | {0x26, 0x72}, |
186 | {0x50, 0x97}, |
187 | {0x51, 0x1f}, |
188 | {0x52, 0x00}, |
189 | {0x53, 0x00}, |
190 | {0x20, 0x00}, |
191 | {0x21, 0xcf}, |
192 | {0x50, 0x4b}, |
193 | {0x38, 0x14}, |
194 | {0xe9, 0x00}, |
195 | {0x56, 0x55}, |
196 | {0x57, 0xff}, |
197 | {0x58, 0xff}, |
198 | {0x59, 0xff}, |
199 | {0x5f, 0x04}, |
200 | {0xec, 0x00}, |
201 | {0x13, 0xff}, |
202 | |
203 | {0x81, 0x3f}, |
204 | {0x82, 0x32}, |
205 | {0x38, 0x11}, |
206 | {0x84, 0x70}, |
207 | {0x85, 0x00}, |
208 | {0x86, 0x03}, |
209 | {0x87, 0x01}, |
210 | {0x88, 0x05}, |
211 | {0x89, 0x30}, |
212 | {0x8d, 0x30}, |
213 | {0x8f, 0x85}, |
214 | {0x93, 0x30}, |
215 | {0x95, 0x85}, |
216 | {0x99, 0x30}, |
217 | {0x9b, 0x85}, |
218 | |
219 | {0x9c, 0x08}, |
220 | {0x9d, 0x12}, |
221 | {0x9e, 0x23}, |
222 | {0x9f, 0x45}, |
223 | {0xa0, 0x55}, |
224 | {0xa1, 0x64}, |
225 | {0xa2, 0x72}, |
226 | {0xa3, 0x7f}, |
227 | {0xa4, 0x8b}, |
228 | {0xa5, 0x95}, |
229 | {0xa6, 0xa7}, |
230 | {0xa7, 0xb5}, |
231 | {0xa8, 0xcb}, |
232 | {0xa9, 0xdd}, |
233 | {0xaa, 0xec}, |
234 | {0xab, 0x1a}, |
235 | |
236 | {0xce, 0x78}, |
237 | {0xcf, 0x6e}, |
238 | {0xd0, 0x0a}, |
239 | {0xd1, 0x0c}, |
240 | {0xd2, 0x84}, |
241 | {0xd3, 0x90}, |
242 | {0xd4, 0x1e}, |
243 | |
244 | {0x5a, 0x24}, |
245 | {0x5b, 0x1f}, |
246 | {0x5c, 0x88}, |
247 | {0x5d, 0x60}, |
248 | |
249 | {0xac, 0x6e}, |
250 | {0xbe, 0xff}, |
251 | {0xbf, 0x00}, |
252 | |
253 | {0x0f, 0x1d}, |
254 | {0x0f, 0x1f}, |
255 | }; |
256 | |
257 | static const struct ov7740_framesize ov7740_framesizes[] = { |
258 | { |
259 | .width = VGA_WIDTH, |
260 | .height = VGA_HEIGHT, |
261 | .regs = ov7740_vga, |
262 | .reg_num = ARRAY_SIZE(ov7740_vga), |
263 | }, |
264 | }; |
265 | |
266 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
267 | static int ov7740_get_register(struct v4l2_subdev *sd, |
268 | struct v4l2_dbg_register *reg) |
269 | { |
270 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
271 | struct regmap *regmap = ov7740->regmap; |
272 | unsigned int val = 0; |
273 | int ret; |
274 | |
275 | ret = regmap_read(map: regmap, reg: reg->reg & 0xff, val: &val); |
276 | reg->val = val; |
277 | reg->size = 1; |
278 | |
279 | return ret; |
280 | } |
281 | |
282 | static int ov7740_set_register(struct v4l2_subdev *sd, |
283 | const struct v4l2_dbg_register *reg) |
284 | { |
285 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
286 | struct regmap *regmap = ov7740->regmap; |
287 | |
288 | regmap_write(map: regmap, reg: reg->reg & 0xff, val: reg->val & 0xff); |
289 | |
290 | return 0; |
291 | } |
292 | #endif |
293 | |
294 | static int ov7740_set_power(struct ov7740 *ov7740, int on) |
295 | { |
296 | int ret; |
297 | |
298 | if (on) { |
299 | ret = clk_prepare_enable(clk: ov7740->xvclk); |
300 | if (ret) |
301 | return ret; |
302 | |
303 | if (ov7740->pwdn_gpio) |
304 | gpiod_direction_output(desc: ov7740->pwdn_gpio, value: 0); |
305 | |
306 | if (ov7740->resetb_gpio) { |
307 | gpiod_set_value(desc: ov7740->resetb_gpio, value: 1); |
308 | usleep_range(min: 500, max: 1000); |
309 | gpiod_set_value(desc: ov7740->resetb_gpio, value: 0); |
310 | usleep_range(min: 3000, max: 5000); |
311 | } |
312 | } else { |
313 | clk_disable_unprepare(clk: ov7740->xvclk); |
314 | |
315 | if (ov7740->pwdn_gpio) |
316 | gpiod_direction_output(desc: ov7740->pwdn_gpio, value: 0); |
317 | } |
318 | |
319 | return 0; |
320 | } |
321 | |
322 | static const struct v4l2_subdev_core_ops ov7740_subdev_core_ops = { |
323 | .log_status = v4l2_ctrl_subdev_log_status, |
324 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
325 | .g_register = ov7740_get_register, |
326 | .s_register = ov7740_set_register, |
327 | #endif |
328 | .subscribe_event = v4l2_ctrl_subdev_subscribe_event, |
329 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, |
330 | }; |
331 | |
332 | static int ov7740_set_white_balance(struct ov7740 *ov7740, int awb) |
333 | { |
334 | struct regmap *regmap = ov7740->regmap; |
335 | unsigned int value; |
336 | int ret; |
337 | |
338 | ret = regmap_read(map: regmap, REG_ISP_CTRL00, val: &value); |
339 | if (!ret) { |
340 | if (awb) |
341 | value |= (ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN); |
342 | else |
343 | value &= ~(ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN); |
344 | ret = regmap_write(map: regmap, REG_ISP_CTRL00, val: value); |
345 | if (ret) |
346 | return ret; |
347 | } |
348 | |
349 | if (!awb) { |
350 | ret = regmap_write(map: regmap, REG_BGAIN, |
351 | val: ov7740->blue_balance->val); |
352 | if (ret) |
353 | return ret; |
354 | |
355 | ret = regmap_write(map: regmap, REG_RGAIN, val: ov7740->red_balance->val); |
356 | if (ret) |
357 | return ret; |
358 | } |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | static int ov7740_set_saturation(struct regmap *regmap, int value) |
364 | { |
365 | int ret; |
366 | |
367 | ret = regmap_write(map: regmap, REG_USAT, val: (unsigned char)value); |
368 | if (ret) |
369 | return ret; |
370 | |
371 | return regmap_write(map: regmap, REG_VSAT, val: (unsigned char)value); |
372 | } |
373 | |
374 | static int ov7740_set_gain(struct regmap *regmap, int value) |
375 | { |
376 | int ret; |
377 | |
378 | ret = regmap_write(map: regmap, REG_GAIN, val: value & 0xff); |
379 | if (ret) |
380 | return ret; |
381 | |
382 | ret = regmap_update_bits(map: regmap, REG_CTRL15, |
383 | REG15_GAIN_MSB, val: (value >> 8) & 0x3); |
384 | if (!ret) |
385 | ret = regmap_update_bits(map: regmap, REG_REG13, REG13_AGC_EN, val: 0); |
386 | |
387 | return ret; |
388 | } |
389 | |
390 | static int ov7740_set_autogain(struct regmap *regmap, int value) |
391 | { |
392 | unsigned int reg; |
393 | int ret; |
394 | |
395 | ret = regmap_read(map: regmap, REG_REG13, val: ®); |
396 | if (ret) |
397 | return ret; |
398 | if (value) |
399 | reg |= REG13_AGC_EN; |
400 | else |
401 | reg &= ~REG13_AGC_EN; |
402 | return regmap_write(map: regmap, REG_REG13, val: reg); |
403 | } |
404 | |
405 | static int ov7740_set_brightness(struct regmap *regmap, int value) |
406 | { |
407 | /* Turn off AEC/AGC */ |
408 | regmap_update_bits(map: regmap, REG_REG13, REG13_AEC_EN, val: 0); |
409 | regmap_update_bits(map: regmap, REG_REG13, REG13_AGC_EN, val: 0); |
410 | |
411 | if (value >= 0) { |
412 | regmap_write(map: regmap, REG_YBRIGHT, val: (unsigned char)value); |
413 | regmap_update_bits(map: regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, val: 0); |
414 | } else{ |
415 | regmap_write(map: regmap, REG_YBRIGHT, val: (unsigned char)(-value)); |
416 | regmap_update_bits(map: regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, val: 1); |
417 | } |
418 | |
419 | return 0; |
420 | } |
421 | |
422 | static int ov7740_set_contrast(struct regmap *regmap, int value) |
423 | { |
424 | return regmap_write(map: regmap, REG_YGAIN, val: (unsigned char)value); |
425 | } |
426 | |
427 | static int ov7740_get_gain(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl) |
428 | { |
429 | struct regmap *regmap = ov7740->regmap; |
430 | unsigned int value0, value1; |
431 | int ret; |
432 | |
433 | if (!ctrl->val) |
434 | return 0; |
435 | |
436 | ret = regmap_read(map: regmap, REG_GAIN, val: &value0); |
437 | if (ret) |
438 | return ret; |
439 | ret = regmap_read(map: regmap, REG_CTRL15, val: &value1); |
440 | if (ret) |
441 | return ret; |
442 | |
443 | ov7740->gain->val = (value1 << 8) | (value0 & 0xff); |
444 | |
445 | return 0; |
446 | } |
447 | |
448 | static int ov7740_get_exp(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl) |
449 | { |
450 | struct regmap *regmap = ov7740->regmap; |
451 | unsigned int value0, value1; |
452 | int ret; |
453 | |
454 | if (ctrl->val == V4L2_EXPOSURE_MANUAL) |
455 | return 0; |
456 | |
457 | ret = regmap_read(map: regmap, REG_AEC, val: &value0); |
458 | if (ret) |
459 | return ret; |
460 | ret = regmap_read(map: regmap, REG_HAEC, val: &value1); |
461 | if (ret) |
462 | return ret; |
463 | |
464 | ov7740->exposure->val = (value1 << 8) | (value0 & 0xff); |
465 | |
466 | return 0; |
467 | } |
468 | |
469 | static int ov7740_set_exp(struct regmap *regmap, int value) |
470 | { |
471 | int ret; |
472 | |
473 | /* Turn off AEC/AGC */ |
474 | ret = regmap_update_bits(map: regmap, REG_REG13, |
475 | REG13_AEC_EN | REG13_AGC_EN, val: 0); |
476 | if (ret) |
477 | return ret; |
478 | |
479 | ret = regmap_write(map: regmap, REG_AEC, val: (unsigned char)value); |
480 | if (ret) |
481 | return ret; |
482 | |
483 | return regmap_write(map: regmap, REG_HAEC, val: (unsigned char)(value >> 8)); |
484 | } |
485 | |
486 | static int ov7740_set_autoexp(struct regmap *regmap, |
487 | enum v4l2_exposure_auto_type value) |
488 | { |
489 | unsigned int reg; |
490 | int ret; |
491 | |
492 | ret = regmap_read(map: regmap, REG_REG13, val: ®); |
493 | if (!ret) { |
494 | if (value == V4L2_EXPOSURE_AUTO) |
495 | reg |= (REG13_AEC_EN | REG13_AGC_EN); |
496 | else |
497 | reg &= ~(REG13_AEC_EN | REG13_AGC_EN); |
498 | ret = regmap_write(map: regmap, REG_REG13, val: reg); |
499 | } |
500 | |
501 | return ret; |
502 | } |
503 | |
504 | |
505 | static int ov7740_get_volatile_ctrl(struct v4l2_ctrl *ctrl) |
506 | { |
507 | struct ov7740 *ov7740 = container_of(ctrl->handler, |
508 | struct ov7740, ctrl_handler); |
509 | int ret; |
510 | |
511 | switch (ctrl->id) { |
512 | case V4L2_CID_AUTOGAIN: |
513 | ret = ov7740_get_gain(ov7740, ctrl); |
514 | break; |
515 | case V4L2_CID_EXPOSURE_AUTO: |
516 | ret = ov7740_get_exp(ov7740, ctrl); |
517 | break; |
518 | default: |
519 | ret = -EINVAL; |
520 | break; |
521 | } |
522 | return ret; |
523 | } |
524 | |
525 | static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) |
526 | { |
527 | struct ov7740 *ov7740 = container_of(ctrl->handler, |
528 | struct ov7740, ctrl_handler); |
529 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov7740->subdev); |
530 | struct regmap *regmap = ov7740->regmap; |
531 | int ret; |
532 | u8 val; |
533 | |
534 | if (!pm_runtime_get_if_in_use(dev: &client->dev)) |
535 | return 0; |
536 | |
537 | switch (ctrl->id) { |
538 | case V4L2_CID_AUTO_WHITE_BALANCE: |
539 | ret = ov7740_set_white_balance(ov7740, awb: ctrl->val); |
540 | break; |
541 | case V4L2_CID_SATURATION: |
542 | ret = ov7740_set_saturation(regmap, value: ctrl->val); |
543 | break; |
544 | case V4L2_CID_BRIGHTNESS: |
545 | ret = ov7740_set_brightness(regmap, value: ctrl->val); |
546 | break; |
547 | case V4L2_CID_CONTRAST: |
548 | ret = ov7740_set_contrast(regmap, value: ctrl->val); |
549 | break; |
550 | case V4L2_CID_VFLIP: |
551 | val = ctrl->val ? REG0C_IMG_FLIP : 0x00; |
552 | ret = regmap_update_bits(map: regmap, REG_REG0C, |
553 | REG0C_IMG_FLIP, val); |
554 | break; |
555 | case V4L2_CID_HFLIP: |
556 | val = ctrl->val ? REG0C_IMG_MIRROR : 0x00; |
557 | ret = regmap_update_bits(map: regmap, REG_REG0C, |
558 | REG0C_IMG_MIRROR, val); |
559 | break; |
560 | case V4L2_CID_AUTOGAIN: |
561 | if (!ctrl->val) |
562 | ret = ov7740_set_gain(regmap, value: ov7740->gain->val); |
563 | else |
564 | ret = ov7740_set_autogain(regmap, value: ctrl->val); |
565 | break; |
566 | |
567 | case V4L2_CID_EXPOSURE_AUTO: |
568 | if (ctrl->val == V4L2_EXPOSURE_MANUAL) |
569 | ret = ov7740_set_exp(regmap, value: ov7740->exposure->val); |
570 | else |
571 | ret = ov7740_set_autoexp(regmap, value: ctrl->val); |
572 | break; |
573 | default: |
574 | ret = -EINVAL; |
575 | break; |
576 | } |
577 | |
578 | pm_runtime_put(dev: &client->dev); |
579 | |
580 | return ret; |
581 | } |
582 | |
583 | static const struct v4l2_ctrl_ops ov7740_ctrl_ops = { |
584 | .g_volatile_ctrl = ov7740_get_volatile_ctrl, |
585 | .s_ctrl = ov7740_set_ctrl, |
586 | }; |
587 | |
588 | static int ov7740_start_streaming(struct ov7740 *ov7740) |
589 | { |
590 | int ret; |
591 | |
592 | if (ov7740->fmt) { |
593 | ret = regmap_multi_reg_write(map: ov7740->regmap, |
594 | regs: ov7740->fmt->regs, |
595 | num_regs: ov7740->fmt->reg_num); |
596 | if (ret) |
597 | return ret; |
598 | } |
599 | |
600 | if (ov7740->frmsize) { |
601 | ret = regmap_multi_reg_write(map: ov7740->regmap, |
602 | regs: ov7740->frmsize->regs, |
603 | num_regs: ov7740->frmsize->reg_num); |
604 | if (ret) |
605 | return ret; |
606 | } |
607 | |
608 | return __v4l2_ctrl_handler_setup(hdl: ov7740->subdev.ctrl_handler); |
609 | } |
610 | |
611 | static int ov7740_set_stream(struct v4l2_subdev *sd, int enable) |
612 | { |
613 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
614 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
615 | int ret = 0; |
616 | |
617 | mutex_lock(&ov7740->mutex); |
618 | |
619 | if (enable) { |
620 | ret = pm_runtime_resume_and_get(dev: &client->dev); |
621 | if (ret < 0) |
622 | goto err_unlock; |
623 | |
624 | ret = ov7740_start_streaming(ov7740); |
625 | if (ret) |
626 | goto err_rpm_put; |
627 | } else { |
628 | pm_runtime_put(dev: &client->dev); |
629 | } |
630 | |
631 | mutex_unlock(lock: &ov7740->mutex); |
632 | return ret; |
633 | |
634 | err_rpm_put: |
635 | pm_runtime_put(dev: &client->dev); |
636 | err_unlock: |
637 | mutex_unlock(lock: &ov7740->mutex); |
638 | return ret; |
639 | } |
640 | |
641 | static const struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { |
642 | .s_stream = ov7740_set_stream, |
643 | }; |
644 | |
645 | static const struct reg_sequence ov7740_format_yuyv[] = { |
646 | {0x12, 0x00}, |
647 | {0x36, 0x3f}, |
648 | {0x80, 0x7f}, |
649 | {0x83, 0x01}, |
650 | }; |
651 | |
652 | static const struct reg_sequence ov7740_format_bggr8[] = { |
653 | {0x36, 0x2f}, |
654 | {0x80, 0x01}, |
655 | {0x83, 0x04}, |
656 | }; |
657 | |
658 | static const struct ov7740_pixfmt ov7740_formats[] = { |
659 | { |
660 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, |
661 | .colorspace = V4L2_COLORSPACE_SRGB, |
662 | .regs = ov7740_format_yuyv, |
663 | .reg_num = ARRAY_SIZE(ov7740_format_yuyv), |
664 | }, |
665 | { |
666 | .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, |
667 | .colorspace = V4L2_COLORSPACE_SRGB, |
668 | .regs = ov7740_format_bggr8, |
669 | .reg_num = ARRAY_SIZE(ov7740_format_bggr8), |
670 | } |
671 | }; |
672 | #define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats) |
673 | |
674 | static int ov7740_enum_mbus_code(struct v4l2_subdev *sd, |
675 | struct v4l2_subdev_state *sd_state, |
676 | struct v4l2_subdev_mbus_code_enum *code) |
677 | { |
678 | if (code->pad || code->index >= N_OV7740_FMTS) |
679 | return -EINVAL; |
680 | |
681 | code->code = ov7740_formats[code->index].mbus_code; |
682 | |
683 | return 0; |
684 | } |
685 | |
686 | static int ov7740_enum_frame_interval(struct v4l2_subdev *sd, |
687 | struct v4l2_subdev_state *sd_state, |
688 | struct v4l2_subdev_frame_interval_enum *fie) |
689 | { |
690 | if (fie->pad) |
691 | return -EINVAL; |
692 | |
693 | if (fie->index >= 1) |
694 | return -EINVAL; |
695 | |
696 | if ((fie->width != VGA_WIDTH) || (fie->height != VGA_HEIGHT)) |
697 | return -EINVAL; |
698 | |
699 | fie->interval.numerator = 1; |
700 | fie->interval.denominator = 60; |
701 | |
702 | return 0; |
703 | } |
704 | |
705 | static int ov7740_enum_frame_size(struct v4l2_subdev *sd, |
706 | struct v4l2_subdev_state *sd_state, |
707 | struct v4l2_subdev_frame_size_enum *fse) |
708 | { |
709 | if (fse->pad) |
710 | return -EINVAL; |
711 | |
712 | if (fse->index > 0) |
713 | return -EINVAL; |
714 | |
715 | fse->min_width = fse->max_width = VGA_WIDTH; |
716 | fse->min_height = fse->max_height = VGA_HEIGHT; |
717 | |
718 | return 0; |
719 | } |
720 | |
721 | static int ov7740_try_fmt_internal(struct v4l2_subdev *sd, |
722 | struct v4l2_mbus_framefmt *fmt, |
723 | const struct ov7740_pixfmt **ret_fmt, |
724 | const struct ov7740_framesize **ret_frmsize) |
725 | { |
726 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
727 | const struct ov7740_framesize *fsize = &ov7740_framesizes[0]; |
728 | int index, i; |
729 | |
730 | for (index = 0; index < N_OV7740_FMTS; index++) { |
731 | if (ov7740_formats[index].mbus_code == fmt->code) |
732 | break; |
733 | } |
734 | if (index >= N_OV7740_FMTS) { |
735 | /* default to first format */ |
736 | index = 0; |
737 | fmt->code = ov7740_formats[0].mbus_code; |
738 | } |
739 | if (ret_fmt != NULL) |
740 | *ret_fmt = ov7740_formats + index; |
741 | |
742 | for (i = 0; i < ARRAY_SIZE(ov7740_framesizes); i++) { |
743 | if ((fsize->width >= fmt->width) && |
744 | (fsize->height >= fmt->height)) { |
745 | fmt->width = fsize->width; |
746 | fmt->height = fsize->height; |
747 | break; |
748 | } |
749 | |
750 | fsize++; |
751 | } |
752 | if (i >= ARRAY_SIZE(ov7740_framesizes)) { |
753 | fsize = &ov7740_framesizes[0]; |
754 | fmt->width = fsize->width; |
755 | fmt->height = fsize->height; |
756 | } |
757 | if (ret_frmsize != NULL) |
758 | *ret_frmsize = fsize; |
759 | |
760 | fmt->field = V4L2_FIELD_NONE; |
761 | fmt->colorspace = ov7740_formats[index].colorspace; |
762 | |
763 | ov7740->format = *fmt; |
764 | |
765 | return 0; |
766 | } |
767 | |
768 | static int ov7740_set_fmt(struct v4l2_subdev *sd, |
769 | struct v4l2_subdev_state *sd_state, |
770 | struct v4l2_subdev_format *format) |
771 | { |
772 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
773 | const struct ov7740_pixfmt *ovfmt; |
774 | const struct ov7740_framesize *fsize; |
775 | struct v4l2_mbus_framefmt *mbus_fmt; |
776 | int ret; |
777 | |
778 | mutex_lock(&ov7740->mutex); |
779 | if (format->pad) { |
780 | ret = -EINVAL; |
781 | goto error; |
782 | } |
783 | |
784 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
785 | ret = ov7740_try_fmt_internal(sd, fmt: &format->format, NULL, NULL); |
786 | if (ret) |
787 | goto error; |
788 | |
789 | mbus_fmt = v4l2_subdev_state_get_format(sd_state, format->pad); |
790 | *mbus_fmt = format->format; |
791 | mutex_unlock(lock: &ov7740->mutex); |
792 | return 0; |
793 | } |
794 | |
795 | ret = ov7740_try_fmt_internal(sd, fmt: &format->format, ret_fmt: &ovfmt, ret_frmsize: &fsize); |
796 | if (ret) |
797 | goto error; |
798 | |
799 | ov7740->fmt = ovfmt; |
800 | ov7740->frmsize = fsize; |
801 | |
802 | mutex_unlock(lock: &ov7740->mutex); |
803 | return 0; |
804 | |
805 | error: |
806 | mutex_unlock(lock: &ov7740->mutex); |
807 | return ret; |
808 | } |
809 | |
810 | static int ov7740_get_fmt(struct v4l2_subdev *sd, |
811 | struct v4l2_subdev_state *sd_state, |
812 | struct v4l2_subdev_format *format) |
813 | { |
814 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
815 | struct v4l2_mbus_framefmt *mbus_fmt; |
816 | |
817 | mutex_lock(&ov7740->mutex); |
818 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
819 | mbus_fmt = v4l2_subdev_state_get_format(sd_state, 0); |
820 | format->format = *mbus_fmt; |
821 | } else { |
822 | format->format = ov7740->format; |
823 | } |
824 | mutex_unlock(lock: &ov7740->mutex); |
825 | |
826 | return 0; |
827 | } |
828 | |
829 | static int ov7740_get_frame_interval(struct v4l2_subdev *sd, |
830 | struct v4l2_subdev_state *sd_state, |
831 | struct v4l2_subdev_frame_interval *ival) |
832 | { |
833 | struct v4l2_fract *tpf = &ival->interval; |
834 | |
835 | tpf->numerator = 1; |
836 | tpf->denominator = 60; |
837 | |
838 | return 0; |
839 | } |
840 | |
841 | static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = { |
842 | .enum_frame_interval = ov7740_enum_frame_interval, |
843 | .enum_frame_size = ov7740_enum_frame_size, |
844 | .enum_mbus_code = ov7740_enum_mbus_code, |
845 | .get_fmt = ov7740_get_fmt, |
846 | .set_fmt = ov7740_set_fmt, |
847 | .get_frame_interval = ov7740_get_frame_interval, |
848 | .set_frame_interval = ov7740_get_frame_interval, |
849 | }; |
850 | |
851 | static const struct v4l2_subdev_ops ov7740_subdev_ops = { |
852 | .core = &ov7740_subdev_core_ops, |
853 | .video = &ov7740_subdev_video_ops, |
854 | .pad = &ov7740_subdev_pad_ops, |
855 | }; |
856 | |
857 | static void ov7740_get_default_format(struct v4l2_subdev *sd, |
858 | struct v4l2_mbus_framefmt *format) |
859 | { |
860 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
861 | |
862 | format->width = ov7740->frmsize->width; |
863 | format->height = ov7740->frmsize->height; |
864 | format->colorspace = ov7740->fmt->colorspace; |
865 | format->code = ov7740->fmt->mbus_code; |
866 | format->field = V4L2_FIELD_NONE; |
867 | } |
868 | |
869 | static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
870 | { |
871 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
872 | struct v4l2_mbus_framefmt *format = |
873 | v4l2_subdev_state_get_format(fh->state, 0); |
874 | |
875 | mutex_lock(&ov7740->mutex); |
876 | ov7740_get_default_format(sd, format); |
877 | mutex_unlock(lock: &ov7740->mutex); |
878 | |
879 | return 0; |
880 | } |
881 | |
882 | static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = { |
883 | .open = ov7740_open, |
884 | }; |
885 | |
886 | static int ov7740_probe_dt(struct i2c_client *client, |
887 | struct ov7740 *ov7740) |
888 | { |
889 | ov7740->resetb_gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "reset" , |
890 | flags: GPIOD_OUT_HIGH); |
891 | if (IS_ERR(ptr: ov7740->resetb_gpio)) { |
892 | dev_info(&client->dev, "can't get %s GPIO\n" , "reset" ); |
893 | return PTR_ERR(ptr: ov7740->resetb_gpio); |
894 | } |
895 | |
896 | ov7740->pwdn_gpio = devm_gpiod_get_optional(dev: &client->dev, con_id: "powerdown" , |
897 | flags: GPIOD_OUT_LOW); |
898 | if (IS_ERR(ptr: ov7740->pwdn_gpio)) { |
899 | dev_info(&client->dev, "can't get %s GPIO\n" , "powerdown" ); |
900 | return PTR_ERR(ptr: ov7740->pwdn_gpio); |
901 | } |
902 | |
903 | return 0; |
904 | } |
905 | |
906 | static int ov7740_detect(struct ov7740 *ov7740) |
907 | { |
908 | struct regmap *regmap = ov7740->regmap; |
909 | unsigned int midh, midl, pidh, pidl; |
910 | int ret; |
911 | |
912 | ret = regmap_read(map: regmap, REG_MIDH, val: &midh); |
913 | if (ret) |
914 | return ret; |
915 | if (midh != 0x7f) |
916 | return -ENODEV; |
917 | |
918 | ret = regmap_read(map: regmap, REG_MIDL, val: &midl); |
919 | if (ret) |
920 | return ret; |
921 | if (midl != 0xa2) |
922 | return -ENODEV; |
923 | |
924 | ret = regmap_read(map: regmap, REG_PIDH, val: &pidh); |
925 | if (ret) |
926 | return ret; |
927 | if (pidh != 0x77) |
928 | return -ENODEV; |
929 | |
930 | ret = regmap_read(map: regmap, REG_PIDL, val: &pidl); |
931 | if (ret) |
932 | return ret; |
933 | if ((pidl != 0x40) && (pidl != 0x41) && (pidl != 0x42)) |
934 | return -ENODEV; |
935 | |
936 | return 0; |
937 | } |
938 | |
939 | static int ov7740_init_controls(struct ov7740 *ov7740) |
940 | { |
941 | struct i2c_client *client = v4l2_get_subdevdata(sd: &ov7740->subdev); |
942 | struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler; |
943 | int ret; |
944 | |
945 | ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); |
946 | if (ret < 0) |
947 | return ret; |
948 | |
949 | ctrl_hdlr->lock = &ov7740->mutex; |
950 | ov7740->auto_wb = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
951 | V4L2_CID_AUTO_WHITE_BALANCE, |
952 | min: 0, max: 1, step: 1, def: 1); |
953 | ov7740->blue_balance = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
954 | V4L2_CID_BLUE_BALANCE, |
955 | min: 0, max: 0xff, step: 1, def: 0x80); |
956 | ov7740->red_balance = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
957 | V4L2_CID_RED_BALANCE, |
958 | min: 0, max: 0xff, step: 1, def: 0x80); |
959 | |
960 | ov7740->brightness = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
961 | V4L2_CID_BRIGHTNESS, |
962 | min: -255, max: 255, step: 1, def: 0); |
963 | ov7740->contrast = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
964 | V4L2_CID_CONTRAST, |
965 | min: 0, max: 127, step: 1, def: 0x20); |
966 | ov7740->saturation = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
967 | V4L2_CID_SATURATION, min: 0, max: 256, step: 1, def: 0x80); |
968 | ov7740->hflip = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
969 | V4L2_CID_HFLIP, min: 0, max: 1, step: 1, def: 0); |
970 | ov7740->vflip = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
971 | V4L2_CID_VFLIP, min: 0, max: 1, step: 1, def: 0); |
972 | |
973 | ov7740->gain = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
974 | V4L2_CID_GAIN, min: 0, max: 1023, step: 1, def: 500); |
975 | |
976 | ov7740->auto_gain = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
977 | V4L2_CID_AUTOGAIN, min: 0, max: 1, step: 1, def: 1); |
978 | |
979 | ov7740->exposure = v4l2_ctrl_new_std(hdl: ctrl_hdlr, ops: &ov7740_ctrl_ops, |
980 | V4L2_CID_EXPOSURE, min: 0, max: 65535, step: 1, def: 500); |
981 | |
982 | ov7740->auto_exposure = v4l2_ctrl_new_std_menu(hdl: ctrl_hdlr, |
983 | ops: &ov7740_ctrl_ops, |
984 | V4L2_CID_EXPOSURE_AUTO, |
985 | max: V4L2_EXPOSURE_MANUAL, mask: 0, |
986 | def: V4L2_EXPOSURE_AUTO); |
987 | |
988 | v4l2_ctrl_auto_cluster(ncontrols: 3, controls: &ov7740->auto_wb, manual_val: 0, set_volatile: false); |
989 | v4l2_ctrl_auto_cluster(ncontrols: 2, controls: &ov7740->auto_gain, manual_val: 0, set_volatile: true); |
990 | v4l2_ctrl_auto_cluster(ncontrols: 2, controls: &ov7740->auto_exposure, |
991 | manual_val: V4L2_EXPOSURE_MANUAL, set_volatile: true); |
992 | |
993 | if (ctrl_hdlr->error) { |
994 | ret = ctrl_hdlr->error; |
995 | dev_err(&client->dev, "controls initialisation failed (%d)\n" , |
996 | ret); |
997 | goto error; |
998 | } |
999 | |
1000 | ret = v4l2_ctrl_handler_setup(hdl: ctrl_hdlr); |
1001 | if (ret) { |
1002 | dev_err(&client->dev, "%s control init failed (%d)\n" , |
1003 | __func__, ret); |
1004 | goto error; |
1005 | } |
1006 | |
1007 | ov7740->subdev.ctrl_handler = ctrl_hdlr; |
1008 | return 0; |
1009 | |
1010 | error: |
1011 | v4l2_ctrl_handler_free(hdl: ctrl_hdlr); |
1012 | mutex_destroy(lock: &ov7740->mutex); |
1013 | return ret; |
1014 | } |
1015 | |
1016 | static void ov7740_free_controls(struct ov7740 *ov7740) |
1017 | { |
1018 | v4l2_ctrl_handler_free(hdl: ov7740->subdev.ctrl_handler); |
1019 | mutex_destroy(lock: &ov7740->mutex); |
1020 | } |
1021 | |
1022 | #define OV7740_MAX_REGISTER 0xff |
1023 | static const struct regmap_config ov7740_regmap_config = { |
1024 | .reg_bits = 8, |
1025 | .val_bits = 8, |
1026 | .max_register = OV7740_MAX_REGISTER, |
1027 | }; |
1028 | |
1029 | static int ov7740_probe(struct i2c_client *client) |
1030 | { |
1031 | struct ov7740 *ov7740; |
1032 | struct v4l2_subdev *sd; |
1033 | int ret; |
1034 | |
1035 | ov7740 = devm_kzalloc(dev: &client->dev, size: sizeof(*ov7740), GFP_KERNEL); |
1036 | if (!ov7740) |
1037 | return -ENOMEM; |
1038 | |
1039 | ov7740->xvclk = devm_clk_get(dev: &client->dev, id: "xvclk" ); |
1040 | if (IS_ERR(ptr: ov7740->xvclk)) { |
1041 | ret = PTR_ERR(ptr: ov7740->xvclk); |
1042 | dev_err(&client->dev, |
1043 | "OV7740: fail to get xvclk: %d\n" , ret); |
1044 | return ret; |
1045 | } |
1046 | |
1047 | ret = ov7740_probe_dt(client, ov7740); |
1048 | if (ret) |
1049 | return ret; |
1050 | |
1051 | ov7740->regmap = devm_regmap_init_sccb(client, &ov7740_regmap_config); |
1052 | if (IS_ERR(ptr: ov7740->regmap)) { |
1053 | ret = PTR_ERR(ptr: ov7740->regmap); |
1054 | dev_err(&client->dev, "Failed to allocate register map: %d\n" , |
1055 | ret); |
1056 | return ret; |
1057 | } |
1058 | |
1059 | sd = &ov7740->subdev; |
1060 | v4l2_i2c_subdev_init(sd, client, ops: &ov7740_subdev_ops); |
1061 | |
1062 | sd->internal_ops = &ov7740_subdev_internal_ops; |
1063 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
1064 | |
1065 | ov7740->pad.flags = MEDIA_PAD_FL_SOURCE; |
1066 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; |
1067 | ret = media_entity_pads_init(entity: &sd->entity, num_pads: 1, pads: &ov7740->pad); |
1068 | if (ret) |
1069 | return ret; |
1070 | |
1071 | ret = ov7740_set_power(ov7740, on: 1); |
1072 | if (ret) |
1073 | return ret; |
1074 | |
1075 | pm_runtime_set_active(dev: &client->dev); |
1076 | pm_runtime_enable(dev: &client->dev); |
1077 | |
1078 | ret = ov7740_detect(ov7740); |
1079 | if (ret) |
1080 | goto error_detect; |
1081 | |
1082 | mutex_init(&ov7740->mutex); |
1083 | |
1084 | ret = ov7740_init_controls(ov7740); |
1085 | if (ret) |
1086 | goto error_init_controls; |
1087 | |
1088 | v4l_info(client, "chip found @ 0x%02x (%s)\n" , |
1089 | client->addr << 1, client->adapter->name); |
1090 | |
1091 | ov7740->fmt = &ov7740_formats[0]; |
1092 | ov7740->frmsize = &ov7740_framesizes[0]; |
1093 | |
1094 | ov7740_get_default_format(sd, format: &ov7740->format); |
1095 | |
1096 | ret = v4l2_async_register_subdev(sd); |
1097 | if (ret) |
1098 | goto error_async_register; |
1099 | |
1100 | pm_runtime_idle(dev: &client->dev); |
1101 | |
1102 | return 0; |
1103 | |
1104 | error_async_register: |
1105 | v4l2_ctrl_handler_free(hdl: ov7740->subdev.ctrl_handler); |
1106 | error_init_controls: |
1107 | ov7740_free_controls(ov7740); |
1108 | error_detect: |
1109 | pm_runtime_disable(dev: &client->dev); |
1110 | pm_runtime_set_suspended(dev: &client->dev); |
1111 | ov7740_set_power(ov7740, on: 0); |
1112 | media_entity_cleanup(entity: &ov7740->subdev.entity); |
1113 | |
1114 | return ret; |
1115 | } |
1116 | |
1117 | static void ov7740_remove(struct i2c_client *client) |
1118 | { |
1119 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
1120 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
1121 | |
1122 | mutex_destroy(lock: &ov7740->mutex); |
1123 | v4l2_ctrl_handler_free(hdl: ov7740->subdev.ctrl_handler); |
1124 | media_entity_cleanup(entity: &ov7740->subdev.entity); |
1125 | v4l2_async_unregister_subdev(sd); |
1126 | ov7740_free_controls(ov7740); |
1127 | |
1128 | pm_runtime_get_sync(dev: &client->dev); |
1129 | pm_runtime_disable(dev: &client->dev); |
1130 | pm_runtime_set_suspended(dev: &client->dev); |
1131 | pm_runtime_put_noidle(dev: &client->dev); |
1132 | |
1133 | ov7740_set_power(ov7740, on: 0); |
1134 | } |
1135 | |
1136 | static int __maybe_unused ov7740_runtime_suspend(struct device *dev) |
1137 | { |
1138 | struct v4l2_subdev *sd = dev_get_drvdata(dev); |
1139 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
1140 | |
1141 | ov7740_set_power(ov7740, on: 0); |
1142 | |
1143 | return 0; |
1144 | } |
1145 | |
1146 | static int __maybe_unused ov7740_runtime_resume(struct device *dev) |
1147 | { |
1148 | struct v4l2_subdev *sd = dev_get_drvdata(dev); |
1149 | struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); |
1150 | |
1151 | return ov7740_set_power(ov7740, on: 1); |
1152 | } |
1153 | |
1154 | static const struct i2c_device_id ov7740_id[] = { |
1155 | { "ov7740" , 0 }, |
1156 | { /* sentinel */ } |
1157 | }; |
1158 | MODULE_DEVICE_TABLE(i2c, ov7740_id); |
1159 | |
1160 | static const struct dev_pm_ops ov7740_pm_ops = { |
1161 | SET_RUNTIME_PM_OPS(ov7740_runtime_suspend, ov7740_runtime_resume, NULL) |
1162 | }; |
1163 | |
1164 | static const struct of_device_id ov7740_of_match[] = { |
1165 | {.compatible = "ovti,ov7740" , }, |
1166 | { /* sentinel */ }, |
1167 | }; |
1168 | MODULE_DEVICE_TABLE(of, ov7740_of_match); |
1169 | |
1170 | static struct i2c_driver ov7740_i2c_driver = { |
1171 | .driver = { |
1172 | .name = "ov7740" , |
1173 | .pm = &ov7740_pm_ops, |
1174 | .of_match_table = ov7740_of_match, |
1175 | }, |
1176 | .probe = ov7740_probe, |
1177 | .remove = ov7740_remove, |
1178 | .id_table = ov7740_id, |
1179 | }; |
1180 | module_i2c_driver(ov7740_i2c_driver); |
1181 | |
1182 | MODULE_DESCRIPTION("The V4L2 driver for Omnivision 7740 sensor" ); |
1183 | MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>" ); |
1184 | MODULE_LICENSE("GPL v2" ); |
1185 | |