rdacm21.c 15 KB

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