1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * IMI RDACM21 GMSL Camera Driver |
4 | * |
5 | * Copyright (C) 2017-2020 Jacopo Mondi |
6 | * Copyright (C) 2017-2019 Kieran Bingham |
7 | * Copyright (C) 2017-2019 Laurent Pinchart |
8 | * Copyright (C) 2017-2019 Niklas Söderlund |
9 | * Copyright (C) 2016 Renesas Electronics Corporation |
10 | * Copyright (C) 2015 Cogent Embedded, Inc. |
11 | */ |
12 | |
13 | #include <linux/delay.h> |
14 | #include <linux/fwnode.h> |
15 | #include <linux/init.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/videodev2.h> |
20 | |
21 | #include <media/v4l2-async.h> |
22 | #include <media/v4l2-ctrls.h> |
23 | #include <media/v4l2-subdev.h> |
24 | #include "max9271.h" |
25 | |
26 | #define MAX9271_RESET_CYCLES 10 |
27 | |
28 | #define OV490_I2C_ADDRESS 0x24 |
29 | |
30 | #define OV490_PAGE_HIGH_REG 0xfffd |
31 | #define OV490_PAGE_LOW_REG 0xfffe |
32 | |
33 | /* |
34 | * The SCCB slave handling is undocumented; the registers naming scheme is |
35 | * totally arbitrary. |
36 | */ |
37 | #define OV490_SCCB_SLAVE_WRITE 0x00 |
38 | #define OV490_SCCB_SLAVE_READ 0x01 |
39 | #define OV490_SCCB_SLAVE0_DIR 0x80195000 |
40 | #define OV490_SCCB_SLAVE0_ADDR_HIGH 0x80195001 |
41 | #define OV490_SCCB_SLAVE0_ADDR_LOW 0x80195002 |
42 | |
43 | #define OV490_DVP_CTRL3 0x80286009 |
44 | |
45 | #define OV490_ODS_CTRL_FRAME_OUTPUT_EN 0x0c |
46 | #define OV490_ODS_CTRL 0x8029d000 |
47 | |
48 | #define OV490_HOST_CMD 0x808000c0 |
49 | #define OV490_HOST_CMD_TRIGGER 0xc1 |
50 | |
51 | #define OV490_ID_VAL 0x0490 |
52 | #define OV490_ID(_p, _v) ((((_p) & 0xff) << 8) | ((_v) & 0xff)) |
53 | #define OV490_PID 0x8080300a |
54 | #define OV490_VER 0x8080300b |
55 | #define OV490_PID_TIMEOUT 20 |
56 | #define OV490_OUTPUT_EN_TIMEOUT 300 |
57 | |
58 | #define OV490_GPIO0 BIT(0) |
59 | #define OV490_SPWDN0 BIT(0) |
60 | #define OV490_GPIO_SEL0 0x80800050 |
61 | #define OV490_GPIO_SEL1 0x80800051 |
62 | #define OV490_GPIO_DIRECTION0 0x80800054 |
63 | #define OV490_GPIO_DIRECTION1 0x80800055 |
64 | #define OV490_GPIO_OUTPUT_VALUE0 0x80800058 |
65 | #define OV490_GPIO_OUTPUT_VALUE1 0x80800059 |
66 | |
67 | #define OV490_ISP_HSIZE_LOW 0x80820060 |
68 | #define OV490_ISP_HSIZE_HIGH 0x80820061 |
69 | #define OV490_ISP_VSIZE_LOW 0x80820062 |
70 | #define OV490_ISP_VSIZE_HIGH 0x80820063 |
71 | |
72 | #define OV10640_PID_TIMEOUT 20 |
73 | #define OV10640_ID_HIGH 0xa6 |
74 | #define OV10640_CHIP_ID 0x300a |
75 | #define OV10640_PIXEL_RATE 55000000 |
76 | |
77 | struct rdacm21_device { |
78 | struct device *dev; |
79 | struct max9271_device serializer; |
80 | struct i2c_client *isp; |
81 | struct v4l2_subdev sd; |
82 | struct media_pad pad; |
83 | struct v4l2_mbus_framefmt fmt; |
84 | struct v4l2_ctrl_handler ctrls; |
85 | u32 addrs[2]; |
86 | u16 last_page; |
87 | }; |
88 | |
89 | static inline struct rdacm21_device *sd_to_rdacm21(struct v4l2_subdev *sd) |
90 | { |
91 | return container_of(sd, struct rdacm21_device, sd); |
92 | } |
93 | |
94 | static const struct ov490_reg { |
95 | u16 reg; |
96 | u8 val; |
97 | } ov490_regs_wizard[] = { |
98 | {0xfffd, 0x80}, |
99 | {0xfffe, 0x82}, |
100 | {0x0071, 0x11}, |
101 | {0x0075, 0x11}, |
102 | {0xfffe, 0x29}, |
103 | {0x6010, 0x01}, |
104 | /* |
105 | * OV490 EMB line disable in YUV and RAW data, |
106 | * NOTE: EMB line is still used in ISP and sensor |
107 | */ |
108 | {0xe000, 0x14}, |
109 | {0xfffe, 0x28}, |
110 | {0x6000, 0x04}, |
111 | {0x6004, 0x00}, |
112 | /* |
113 | * PCLK polarity - useless due to silicon bug. |
114 | * Use 0x808000bb register instead. |
115 | */ |
116 | {0x6008, 0x00}, |
117 | {0xfffe, 0x80}, |
118 | {0x0091, 0x00}, |
119 | /* bit[3]=0 - PCLK polarity workaround. */ |
120 | {0x00bb, 0x1d}, |
121 | /* Ov490 FSIN: app_fsin_from_fsync */ |
122 | {0xfffe, 0x85}, |
123 | {0x0008, 0x00}, |
124 | {0x0009, 0x01}, |
125 | /* FSIN0 source. */ |
126 | {0x000A, 0x05}, |
127 | {0x000B, 0x00}, |
128 | /* FSIN0 delay. */ |
129 | {0x0030, 0x02}, |
130 | {0x0031, 0x00}, |
131 | {0x0032, 0x00}, |
132 | {0x0033, 0x00}, |
133 | /* FSIN1 delay. */ |
134 | {0x0038, 0x02}, |
135 | {0x0039, 0x00}, |
136 | {0x003A, 0x00}, |
137 | {0x003B, 0x00}, |
138 | /* FSIN0 length. */ |
139 | {0x0070, 0x2C}, |
140 | {0x0071, 0x01}, |
141 | {0x0072, 0x00}, |
142 | {0x0073, 0x00}, |
143 | /* FSIN1 length. */ |
144 | {0x0074, 0x64}, |
145 | {0x0075, 0x00}, |
146 | {0x0076, 0x00}, |
147 | {0x0077, 0x00}, |
148 | {0x0000, 0x14}, |
149 | {0x0001, 0x00}, |
150 | {0x0002, 0x00}, |
151 | {0x0003, 0x00}, |
152 | /* |
153 | * Load fsin0,load fsin1,load other, |
154 | * It will be cleared automatically. |
155 | */ |
156 | {0x0004, 0x32}, |
157 | {0x0005, 0x00}, |
158 | {0x0006, 0x00}, |
159 | {0x0007, 0x00}, |
160 | {0xfffe, 0x80}, |
161 | /* Sensor FSIN. */ |
162 | {0x0081, 0x00}, |
163 | /* ov10640 FSIN enable */ |
164 | {0xfffe, 0x19}, |
165 | {0x5000, 0x00}, |
166 | {0x5001, 0x30}, |
167 | {0x5002, 0x8c}, |
168 | {0x5003, 0xb2}, |
169 | {0xfffe, 0x80}, |
170 | {0x00c0, 0xc1}, |
171 | /* ov10640 HFLIP=1 by default */ |
172 | {0xfffe, 0x19}, |
173 | {0x5000, 0x01}, |
174 | {0x5001, 0x00}, |
175 | {0xfffe, 0x80}, |
176 | {0x00c0, 0xdc}, |
177 | }; |
178 | |
179 | static int ov490_read(struct rdacm21_device *dev, u16 reg, u8 *val) |
180 | { |
181 | u8 buf[2] = { reg >> 8, reg }; |
182 | int ret; |
183 | |
184 | ret = i2c_master_send(client: dev->isp, buf, count: 2); |
185 | if (ret == 2) |
186 | ret = i2c_master_recv(client: dev->isp, buf: val, count: 1); |
187 | |
188 | if (ret < 0) { |
189 | dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n" , |
190 | __func__, reg, ret); |
191 | return ret; |
192 | } |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static int ov490_write(struct rdacm21_device *dev, u16 reg, u8 val) |
198 | { |
199 | u8 buf[3] = { reg >> 8, reg, val }; |
200 | int ret; |
201 | |
202 | ret = i2c_master_send(client: dev->isp, buf, count: 3); |
203 | if (ret < 0) { |
204 | dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n" , |
205 | __func__, reg, ret); |
206 | return ret; |
207 | } |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static int ov490_set_page(struct rdacm21_device *dev, u16 page) |
213 | { |
214 | u8 page_high = page >> 8; |
215 | u8 page_low = page; |
216 | int ret; |
217 | |
218 | if (page == dev->last_page) |
219 | return 0; |
220 | |
221 | if (page_high != (dev->last_page >> 8)) { |
222 | ret = ov490_write(dev, OV490_PAGE_HIGH_REG, val: page_high); |
223 | if (ret) |
224 | return ret; |
225 | } |
226 | |
227 | if (page_low != (u8)dev->last_page) { |
228 | ret = ov490_write(dev, OV490_PAGE_LOW_REG, val: page_low); |
229 | if (ret) |
230 | return ret; |
231 | } |
232 | |
233 | dev->last_page = page; |
234 | usleep_range(min: 100, max: 150); |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | static int ov490_read_reg(struct rdacm21_device *dev, u32 reg, u8 *val) |
240 | { |
241 | int ret; |
242 | |
243 | ret = ov490_set_page(dev, page: reg >> 16); |
244 | if (ret) |
245 | return ret; |
246 | |
247 | ret = ov490_read(dev, reg: (u16)reg, val); |
248 | if (ret) |
249 | return ret; |
250 | |
251 | dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n" , __func__, reg, *val); |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | static int ov490_write_reg(struct rdacm21_device *dev, u32 reg, u8 val) |
257 | { |
258 | int ret; |
259 | |
260 | ret = ov490_set_page(dev, page: reg >> 16); |
261 | if (ret) |
262 | return ret; |
263 | |
264 | ret = ov490_write(dev, reg: (u16)reg, val); |
265 | if (ret) |
266 | return ret; |
267 | |
268 | dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n" , __func__, reg, val); |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static int rdacm21_s_stream(struct v4l2_subdev *sd, int enable) |
274 | { |
275 | struct rdacm21_device *dev = sd_to_rdacm21(sd); |
276 | |
277 | /* |
278 | * Enable serial link now that the ISP provides a valid pixel clock |
279 | * to start serializing video data on the GMSL link. |
280 | */ |
281 | return max9271_set_serial_link(dev: &dev->serializer, enable); |
282 | } |
283 | |
284 | static int rdacm21_enum_mbus_code(struct v4l2_subdev *sd, |
285 | struct v4l2_subdev_state *sd_state, |
286 | struct v4l2_subdev_mbus_code_enum *code) |
287 | { |
288 | if (code->pad || code->index > 0) |
289 | return -EINVAL; |
290 | |
291 | code->code = MEDIA_BUS_FMT_YUYV8_1X16; |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static int rdacm21_get_fmt(struct v4l2_subdev *sd, |
297 | struct v4l2_subdev_state *sd_state, |
298 | struct v4l2_subdev_format *format) |
299 | { |
300 | struct v4l2_mbus_framefmt *mf = &format->format; |
301 | struct rdacm21_device *dev = sd_to_rdacm21(sd); |
302 | |
303 | if (format->pad) |
304 | return -EINVAL; |
305 | |
306 | mf->width = dev->fmt.width; |
307 | mf->height = dev->fmt.height; |
308 | mf->code = MEDIA_BUS_FMT_YUYV8_1X16; |
309 | mf->colorspace = V4L2_COLORSPACE_SRGB; |
310 | mf->field = V4L2_FIELD_NONE; |
311 | mf->ycbcr_enc = V4L2_YCBCR_ENC_601; |
312 | mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; |
313 | mf->xfer_func = V4L2_XFER_FUNC_NONE; |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static const struct v4l2_subdev_video_ops rdacm21_video_ops = { |
319 | .s_stream = rdacm21_s_stream, |
320 | }; |
321 | |
322 | static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = { |
323 | .enum_mbus_code = rdacm21_enum_mbus_code, |
324 | .get_fmt = rdacm21_get_fmt, |
325 | .set_fmt = rdacm21_get_fmt, |
326 | }; |
327 | |
328 | static const struct v4l2_subdev_ops rdacm21_subdev_ops = { |
329 | .video = &rdacm21_video_ops, |
330 | .pad = &rdacm21_subdev_pad_ops, |
331 | }; |
332 | |
333 | static void ov10640_power_up(struct rdacm21_device *dev) |
334 | { |
335 | /* Enable GPIO0#0 (reset) and GPIO1#0 (pwdn) as output lines. */ |
336 | ov490_write_reg(dev, OV490_GPIO_SEL0, OV490_GPIO0); |
337 | ov490_write_reg(dev, OV490_GPIO_SEL1, OV490_SPWDN0); |
338 | ov490_write_reg(dev, OV490_GPIO_DIRECTION0, OV490_GPIO0); |
339 | ov490_write_reg(dev, OV490_GPIO_DIRECTION1, OV490_SPWDN0); |
340 | |
341 | /* Power up OV10640 and then reset it. */ |
342 | ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE1, OV490_SPWDN0); |
343 | usleep_range(min: 1500, max: 3000); |
344 | |
345 | ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, val: 0x00); |
346 | usleep_range(min: 1500, max: 3000); |
347 | ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, OV490_GPIO0); |
348 | usleep_range(min: 3000, max: 5000); |
349 | } |
350 | |
351 | static int ov10640_check_id(struct rdacm21_device *dev) |
352 | { |
353 | unsigned int i; |
354 | u8 val = 0; |
355 | |
356 | /* Read OV10640 ID to test communications. */ |
357 | for (i = 0; i < OV10640_PID_TIMEOUT; ++i) { |
358 | ov490_write_reg(dev, OV490_SCCB_SLAVE0_DIR, |
359 | OV490_SCCB_SLAVE_READ); |
360 | ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_HIGH, |
361 | OV10640_CHIP_ID >> 8); |
362 | ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_LOW, |
363 | OV10640_CHIP_ID & 0xff); |
364 | |
365 | /* |
366 | * Trigger SCCB slave transaction and give it some time |
367 | * to complete. |
368 | */ |
369 | ov490_write_reg(dev, OV490_HOST_CMD, OV490_HOST_CMD_TRIGGER); |
370 | usleep_range(min: 1000, max: 1500); |
371 | |
372 | ov490_read_reg(dev, OV490_SCCB_SLAVE0_DIR, val: &val); |
373 | if (val == OV10640_ID_HIGH) |
374 | break; |
375 | usleep_range(min: 1000, max: 1500); |
376 | } |
377 | if (i == OV10640_PID_TIMEOUT) { |
378 | dev_err(dev->dev, "OV10640 ID mismatch: (0x%02x)\n" , val); |
379 | return -ENODEV; |
380 | } |
381 | |
382 | dev_dbg(dev->dev, "OV10640 ID = 0x%2x\n" , val); |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | static int ov490_initialize(struct rdacm21_device *dev) |
388 | { |
389 | u8 pid, ver, val; |
390 | unsigned int i; |
391 | int ret; |
392 | |
393 | ov10640_power_up(dev); |
394 | |
395 | /* |
396 | * Read OV490 Id to test communications. Give it up to 40msec to |
397 | * exit from reset. |
398 | */ |
399 | for (i = 0; i < OV490_PID_TIMEOUT; ++i) { |
400 | ret = ov490_read_reg(dev, OV490_PID, val: &pid); |
401 | if (ret == 0) |
402 | break; |
403 | usleep_range(min: 1000, max: 2000); |
404 | } |
405 | if (i == OV490_PID_TIMEOUT) { |
406 | dev_err(dev->dev, "OV490 PID read failed (%d)\n" , ret); |
407 | return ret; |
408 | } |
409 | |
410 | ret = ov490_read_reg(dev, OV490_VER, val: &ver); |
411 | if (ret < 0) |
412 | return ret; |
413 | |
414 | if (OV490_ID(pid, ver) != OV490_ID_VAL) { |
415 | dev_err(dev->dev, "OV490 ID mismatch (0x%04x)\n" , |
416 | OV490_ID(pid, ver)); |
417 | return -ENODEV; |
418 | } |
419 | |
420 | /* Wait for firmware boot by reading streamon status. */ |
421 | for (i = 0; i < OV490_OUTPUT_EN_TIMEOUT; ++i) { |
422 | ov490_read_reg(dev, OV490_ODS_CTRL, val: &val); |
423 | if (val == OV490_ODS_CTRL_FRAME_OUTPUT_EN) |
424 | break; |
425 | usleep_range(min: 1000, max: 2000); |
426 | } |
427 | if (i == OV490_OUTPUT_EN_TIMEOUT) { |
428 | dev_err(dev->dev, "Timeout waiting for firmware boot\n" ); |
429 | return -ENODEV; |
430 | } |
431 | |
432 | ret = ov10640_check_id(dev); |
433 | if (ret) |
434 | return ret; |
435 | |
436 | /* Program OV490 with register-value table. */ |
437 | for (i = 0; i < ARRAY_SIZE(ov490_regs_wizard); ++i) { |
438 | ret = ov490_write(dev, reg: ov490_regs_wizard[i].reg, |
439 | val: ov490_regs_wizard[i].val); |
440 | if (ret < 0) { |
441 | dev_err(dev->dev, |
442 | "%s: register %u (0x%04x) write failed (%d)\n" , |
443 | __func__, i, ov490_regs_wizard[i].reg, ret); |
444 | |
445 | return -EIO; |
446 | } |
447 | |
448 | usleep_range(min: 100, max: 150); |
449 | } |
450 | |
451 | /* |
452 | * The ISP is programmed with the content of a serial flash memory. |
453 | * Read the firmware configuration to reflect it through the V4L2 APIs. |
454 | */ |
455 | ov490_read_reg(dev, OV490_ISP_HSIZE_HIGH, val: &val); |
456 | dev->fmt.width = (val & 0xf) << 8; |
457 | ov490_read_reg(dev, OV490_ISP_HSIZE_LOW, val: &val); |
458 | dev->fmt.width |= (val & 0xff); |
459 | |
460 | ov490_read_reg(dev, OV490_ISP_VSIZE_HIGH, val: &val); |
461 | dev->fmt.height = (val & 0xf) << 8; |
462 | ov490_read_reg(dev, OV490_ISP_VSIZE_LOW, val: &val); |
463 | dev->fmt.height |= val & 0xff; |
464 | |
465 | /* Set bus width to 12 bits with [0:11] ordering. */ |
466 | ov490_write_reg(dev, OV490_DVP_CTRL3, val: 0x10); |
467 | |
468 | dev_info(dev->dev, "Identified RDACM21 camera module\n" ); |
469 | |
470 | return 0; |
471 | } |
472 | |
473 | static int rdacm21_initialize(struct rdacm21_device *dev) |
474 | { |
475 | int ret; |
476 | |
477 | max9271_wake_up(dev: &dev->serializer); |
478 | |
479 | /* Enable reverse channel and disable the serial link. */ |
480 | ret = max9271_set_serial_link(dev: &dev->serializer, enable: false); |
481 | if (ret) |
482 | return ret; |
483 | |
484 | /* Configure I2C bus at 105Kbps speed and configure GMSL. */ |
485 | ret = max9271_configure_i2c(dev: &dev->serializer, |
486 | MAX9271_I2CSLVSH_469NS_234NS | |
487 | MAX9271_I2CSLVTO_1024US | |
488 | MAX9271_I2CMSTBT_105KBPS); |
489 | if (ret) |
490 | return ret; |
491 | |
492 | ret = max9271_verify_id(dev: &dev->serializer); |
493 | if (ret) |
494 | return ret; |
495 | |
496 | /* |
497 | * Enable GPIO1 and hold OV490 in reset during max9271 configuration. |
498 | * The reset signal has to be asserted for at least 250 useconds. |
499 | */ |
500 | ret = max9271_enable_gpios(dev: &dev->serializer, MAX9271_GPIO1OUT); |
501 | if (ret) |
502 | return ret; |
503 | |
504 | ret = max9271_clear_gpios(dev: &dev->serializer, MAX9271_GPIO1OUT); |
505 | if (ret) |
506 | return ret; |
507 | usleep_range(min: 250, max: 500); |
508 | |
509 | ret = max9271_configure_gmsl_link(dev: &dev->serializer); |
510 | if (ret) |
511 | return ret; |
512 | |
513 | ret = max9271_set_address(dev: &dev->serializer, addr: dev->addrs[0]); |
514 | if (ret) |
515 | return ret; |
516 | dev->serializer.client->addr = dev->addrs[0]; |
517 | |
518 | ret = max9271_set_translation(dev: &dev->serializer, source: dev->addrs[1], |
519 | OV490_I2C_ADDRESS); |
520 | if (ret) |
521 | return ret; |
522 | dev->isp->addr = dev->addrs[1]; |
523 | |
524 | /* Release OV490 from reset and initialize it. */ |
525 | ret = max9271_set_gpios(dev: &dev->serializer, MAX9271_GPIO1OUT); |
526 | if (ret) |
527 | return ret; |
528 | usleep_range(min: 3000, max: 5000); |
529 | |
530 | ret = ov490_initialize(dev); |
531 | if (ret) |
532 | return ret; |
533 | |
534 | /* |
535 | * Set reverse channel high threshold to increase noise immunity. |
536 | * |
537 | * This should be compensated by increasing the reverse channel |
538 | * amplitude on the remote deserializer side. |
539 | */ |
540 | return max9271_set_high_threshold(dev: &dev->serializer, enable: true); |
541 | } |
542 | |
543 | static int rdacm21_probe(struct i2c_client *client) |
544 | { |
545 | struct rdacm21_device *dev; |
546 | int ret; |
547 | |
548 | dev = devm_kzalloc(dev: &client->dev, size: sizeof(*dev), GFP_KERNEL); |
549 | if (!dev) |
550 | return -ENOMEM; |
551 | dev->dev = &client->dev; |
552 | dev->serializer.client = client; |
553 | |
554 | ret = of_property_read_u32_array(np: client->dev.of_node, propname: "reg" , |
555 | out_values: dev->addrs, sz: 2); |
556 | if (ret < 0) { |
557 | dev_err(dev->dev, "Invalid DT reg property: %d\n" , ret); |
558 | return -EINVAL; |
559 | } |
560 | |
561 | /* Create the dummy I2C client for the sensor. */ |
562 | dev->isp = i2c_new_dummy_device(adapter: client->adapter, OV490_I2C_ADDRESS); |
563 | if (IS_ERR(ptr: dev->isp)) |
564 | return PTR_ERR(ptr: dev->isp); |
565 | |
566 | ret = rdacm21_initialize(dev); |
567 | if (ret < 0) |
568 | goto error; |
569 | |
570 | /* Initialize and register the subdevice. */ |
571 | v4l2_i2c_subdev_init(sd: &dev->sd, client, ops: &rdacm21_subdev_ops); |
572 | dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
573 | |
574 | v4l2_ctrl_handler_init(&dev->ctrls, 1); |
575 | v4l2_ctrl_new_std(hdl: &dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, |
576 | OV10640_PIXEL_RATE, OV10640_PIXEL_RATE, step: 1, |
577 | OV10640_PIXEL_RATE); |
578 | dev->sd.ctrl_handler = &dev->ctrls; |
579 | |
580 | ret = dev->ctrls.error; |
581 | if (ret) |
582 | goto error_free_ctrls; |
583 | |
584 | dev->pad.flags = MEDIA_PAD_FL_SOURCE; |
585 | dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; |
586 | ret = media_entity_pads_init(entity: &dev->sd.entity, num_pads: 1, pads: &dev->pad); |
587 | if (ret < 0) |
588 | goto error_free_ctrls; |
589 | |
590 | ret = v4l2_async_register_subdev(sd: &dev->sd); |
591 | if (ret) |
592 | goto error_free_ctrls; |
593 | |
594 | return 0; |
595 | |
596 | error_free_ctrls: |
597 | v4l2_ctrl_handler_free(hdl: &dev->ctrls); |
598 | error: |
599 | i2c_unregister_device(client: dev->isp); |
600 | |
601 | return ret; |
602 | } |
603 | |
604 | static void rdacm21_remove(struct i2c_client *client) |
605 | { |
606 | struct rdacm21_device *dev = sd_to_rdacm21(sd: i2c_get_clientdata(client)); |
607 | |
608 | v4l2_async_unregister_subdev(sd: &dev->sd); |
609 | v4l2_ctrl_handler_free(hdl: &dev->ctrls); |
610 | i2c_unregister_device(client: dev->isp); |
611 | } |
612 | |
613 | static const struct of_device_id rdacm21_of_ids[] = { |
614 | { .compatible = "imi,rdacm21" }, |
615 | { } |
616 | }; |
617 | MODULE_DEVICE_TABLE(of, rdacm21_of_ids); |
618 | |
619 | static struct i2c_driver rdacm21_i2c_driver = { |
620 | .driver = { |
621 | .name = "rdacm21" , |
622 | .of_match_table = rdacm21_of_ids, |
623 | }, |
624 | .probe = rdacm21_probe, |
625 | .remove = rdacm21_remove, |
626 | }; |
627 | |
628 | module_i2c_driver(rdacm21_i2c_driver); |
629 | |
630 | MODULE_DESCRIPTION("GMSL Camera driver for RDACM21" ); |
631 | MODULE_AUTHOR("Jacopo Mondi" ); |
632 | MODULE_LICENSE("GPL v2" ); |
633 | |