m5602_ov9650.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /*
  2. * Driver for the ov9650 sensor
  3. *
  4. * Copyright (C) 2008 Erik Andrén
  5. * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
  6. * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
  7. *
  8. * Portions of code to USB interface and ALi driver software,
  9. * Copyright (c) 2006 Willem Duinker
  10. * v4l2 interface modeled after the V4L2 driver
  11. * for SN9C10x PC Camera Controllers
  12. *
  13. * This program is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU General Public License as
  15. * published by the Free Software Foundation, version 2.
  16. *
  17. */
  18. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  19. #include "m5602_ov9650.h"
  20. static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl);
  21. static void ov9650_dump_registers(struct sd *sd);
  22. /* Vertically and horizontally flips the image if matched, needed for machines
  23. where the sensor is mounted upside down */
  24. static
  25. const
  26. struct dmi_system_id ov9650_flip_dmi_table[] = {
  27. {
  28. .ident = "ASUS A6Ja",
  29. .matches = {
  30. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  31. DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
  32. }
  33. },
  34. {
  35. .ident = "ASUS A6JC",
  36. .matches = {
  37. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  38. DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
  39. }
  40. },
  41. {
  42. .ident = "ASUS A6K",
  43. .matches = {
  44. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  45. DMI_MATCH(DMI_PRODUCT_NAME, "A6K")
  46. }
  47. },
  48. {
  49. .ident = "ASUS A6Kt",
  50. .matches = {
  51. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  52. DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
  53. }
  54. },
  55. {
  56. .ident = "ASUS A6VA",
  57. .matches = {
  58. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  59. DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")
  60. }
  61. },
  62. {
  63. .ident = "ASUS A6VC",
  64. .matches = {
  65. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  66. DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
  67. }
  68. },
  69. {
  70. .ident = "ASUS A6VM",
  71. .matches = {
  72. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  73. DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
  74. }
  75. },
  76. {
  77. .ident = "ASUS A7V",
  78. .matches = {
  79. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  80. DMI_MATCH(DMI_PRODUCT_NAME, "A7V")
  81. }
  82. },
  83. {
  84. .ident = "Alienware Aurora m9700",
  85. .matches = {
  86. DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
  87. DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
  88. }
  89. },
  90. {}
  91. };
  92. static struct v4l2_pix_format ov9650_modes[] = {
  93. {
  94. 176,
  95. 144,
  96. V4L2_PIX_FMT_SBGGR8,
  97. V4L2_FIELD_NONE,
  98. .sizeimage =
  99. 176 * 144,
  100. .bytesperline = 176,
  101. .colorspace = V4L2_COLORSPACE_SRGB,
  102. .priv = 9
  103. }, {
  104. 320,
  105. 240,
  106. V4L2_PIX_FMT_SBGGR8,
  107. V4L2_FIELD_NONE,
  108. .sizeimage =
  109. 320 * 240,
  110. .bytesperline = 320,
  111. .colorspace = V4L2_COLORSPACE_SRGB,
  112. .priv = 8
  113. }, {
  114. 352,
  115. 288,
  116. V4L2_PIX_FMT_SBGGR8,
  117. V4L2_FIELD_NONE,
  118. .sizeimage =
  119. 352 * 288,
  120. .bytesperline = 352,
  121. .colorspace = V4L2_COLORSPACE_SRGB,
  122. .priv = 9
  123. }, {
  124. 640,
  125. 480,
  126. V4L2_PIX_FMT_SBGGR8,
  127. V4L2_FIELD_NONE,
  128. .sizeimage =
  129. 640 * 480,
  130. .bytesperline = 640,
  131. .colorspace = V4L2_COLORSPACE_SRGB,
  132. .priv = 9
  133. }
  134. };
  135. static const struct v4l2_ctrl_ops ov9650_ctrl_ops = {
  136. .s_ctrl = ov9650_s_ctrl,
  137. };
  138. int ov9650_probe(struct sd *sd)
  139. {
  140. int err = 0;
  141. u8 prod_id = 0, ver_id = 0, i;
  142. struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
  143. if (force_sensor) {
  144. if (force_sensor == OV9650_SENSOR) {
  145. pr_info("Forcing an %s sensor\n", ov9650.name);
  146. goto sensor_found;
  147. }
  148. /* If we want to force another sensor,
  149. don't try to probe this one */
  150. return -ENODEV;
  151. }
  152. PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
  153. /* Run the pre-init before probing the sensor */
  154. for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
  155. u8 data = preinit_ov9650[i][2];
  156. if (preinit_ov9650[i][0] == SENSOR)
  157. err = m5602_write_sensor(sd,
  158. preinit_ov9650[i][1], &data, 1);
  159. else
  160. err = m5602_write_bridge(sd,
  161. preinit_ov9650[i][1], data);
  162. }
  163. if (err < 0)
  164. return err;
  165. if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
  166. return -ENODEV;
  167. if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
  168. return -ENODEV;
  169. if ((prod_id == 0x96) && (ver_id == 0x52)) {
  170. pr_info("Detected an ov9650 sensor\n");
  171. goto sensor_found;
  172. }
  173. return -ENODEV;
  174. sensor_found:
  175. sd->gspca_dev.cam.cam_mode = ov9650_modes;
  176. sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);
  177. return 0;
  178. }
  179. int ov9650_init(struct sd *sd)
  180. {
  181. int i, err = 0;
  182. u8 data;
  183. if (dump_sensor)
  184. ov9650_dump_registers(sd);
  185. for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
  186. data = init_ov9650[i][2];
  187. if (init_ov9650[i][0] == SENSOR)
  188. err = m5602_write_sensor(sd, init_ov9650[i][1],
  189. &data, 1);
  190. else
  191. err = m5602_write_bridge(sd, init_ov9650[i][1], data);
  192. }
  193. return 0;
  194. }
  195. int ov9650_init_controls(struct sd *sd)
  196. {
  197. struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
  198. sd->gspca_dev.vdev.ctrl_handler = hdl;
  199. v4l2_ctrl_handler_init(hdl, 9);
  200. sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
  201. V4L2_CID_AUTO_WHITE_BALANCE,
  202. 0, 1, 1, 1);
  203. sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
  204. V4L2_CID_RED_BALANCE, 0, 255, 1,
  205. RED_GAIN_DEFAULT);
  206. sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
  207. V4L2_CID_BLUE_BALANCE, 0, 255, 1,
  208. BLUE_GAIN_DEFAULT);
  209. sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops,
  210. V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
  211. sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE,
  212. 0, 0x1ff, 4, EXPOSURE_DEFAULT);
  213. sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
  214. V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
  215. sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0,
  216. 0x3ff, 1, GAIN_DEFAULT);
  217. sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP,
  218. 0, 1, 1, 0);
  219. sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP,
  220. 0, 1, 1, 0);
  221. if (hdl->error) {
  222. pr_err("Could not initialize controls\n");
  223. return hdl->error;
  224. }
  225. v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false);
  226. v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);
  227. v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
  228. v4l2_ctrl_cluster(2, &sd->hflip);
  229. return 0;
  230. }
  231. int ov9650_start(struct sd *sd)
  232. {
  233. u8 data;
  234. int i, err = 0;
  235. struct cam *cam = &sd->gspca_dev.cam;
  236. int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
  237. int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
  238. int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
  239. int hor_offs = OV9650_LEFT_OFFSET;
  240. struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
  241. if ((!dmi_check_system(ov9650_flip_dmi_table) &&
  242. sd->vflip->val) ||
  243. (dmi_check_system(ov9650_flip_dmi_table) &&
  244. !sd->vflip->val))
  245. ver_offs--;
  246. if (width <= 320)
  247. hor_offs /= 2;
  248. /* Synthesize the vsync/hsync setup */
  249. for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
  250. if (res_init_ov9650[i][0] == BRIDGE)
  251. err = m5602_write_bridge(sd, res_init_ov9650[i][1],
  252. res_init_ov9650[i][2]);
  253. else if (res_init_ov9650[i][0] == SENSOR) {
  254. data = res_init_ov9650[i][2];
  255. err = m5602_write_sensor(sd,
  256. res_init_ov9650[i][1], &data, 1);
  257. }
  258. }
  259. if (err < 0)
  260. return err;
  261. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
  262. ((ver_offs >> 8) & 0xff));
  263. if (err < 0)
  264. return err;
  265. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
  266. if (err < 0)
  267. return err;
  268. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
  269. if (err < 0)
  270. return err;
  271. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
  272. if (err < 0)
  273. return err;
  274. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
  275. if (err < 0)
  276. return err;
  277. for (i = 0; i < 2 && !err; i++)
  278. err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
  279. if (err < 0)
  280. return err;
  281. err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
  282. if (err < 0)
  283. return err;
  284. err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
  285. if (err < 0)
  286. return err;
  287. err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
  288. (hor_offs >> 8) & 0xff);
  289. if (err < 0)
  290. return err;
  291. err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);
  292. if (err < 0)
  293. return err;
  294. err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
  295. ((width + hor_offs) >> 8) & 0xff);
  296. if (err < 0)
  297. return err;
  298. err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
  299. ((width + hor_offs) & 0xff));
  300. if (err < 0)
  301. return err;
  302. err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
  303. if (err < 0)
  304. return err;
  305. switch (width) {
  306. case 640:
  307. PDEBUG(D_CONF, "Configuring camera for VGA mode");
  308. data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |
  309. OV9650_RAW_RGB_SELECT;
  310. err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
  311. break;
  312. case 352:
  313. PDEBUG(D_CONF, "Configuring camera for CIF mode");
  314. data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |
  315. OV9650_RAW_RGB_SELECT;
  316. err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
  317. break;
  318. case 320:
  319. PDEBUG(D_CONF, "Configuring camera for QVGA mode");
  320. data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |
  321. OV9650_RAW_RGB_SELECT;
  322. err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
  323. break;
  324. case 176:
  325. PDEBUG(D_CONF, "Configuring camera for QCIF mode");
  326. data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |
  327. OV9650_RAW_RGB_SELECT;
  328. err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
  329. break;
  330. }
  331. return err;
  332. }
  333. int ov9650_stop(struct sd *sd)
  334. {
  335. u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;
  336. return m5602_write_sensor(sd, OV9650_COM2, &data, 1);
  337. }
  338. void ov9650_disconnect(struct sd *sd)
  339. {
  340. ov9650_stop(sd);
  341. sd->sensor = NULL;
  342. }
  343. static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
  344. {
  345. struct sd *sd = (struct sd *) gspca_dev;
  346. u8 i2c_data;
  347. int err;
  348. PDEBUG(D_CONF, "Set exposure to %d", val);
  349. /* The 6 MSBs */
  350. i2c_data = (val >> 10) & 0x3f;
  351. err = m5602_write_sensor(sd, OV9650_AECHM,
  352. &i2c_data, 1);
  353. if (err < 0)
  354. return err;
  355. /* The 8 middle bits */
  356. i2c_data = (val >> 2) & 0xff;
  357. err = m5602_write_sensor(sd, OV9650_AECH,
  358. &i2c_data, 1);
  359. if (err < 0)
  360. return err;
  361. /* The 2 LSBs */
  362. i2c_data = val & 0x03;
  363. err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
  364. return err;
  365. }
  366. static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
  367. {
  368. int err;
  369. u8 i2c_data;
  370. struct sd *sd = (struct sd *) gspca_dev;
  371. PDEBUG(D_CONF, "Setting gain to %d", val);
  372. /* The 2 MSB */
  373. /* Read the OV9650_VREF register first to avoid
  374. corrupting the VREF high and low bits */
  375. err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
  376. if (err < 0)
  377. return err;
  378. /* Mask away all uninteresting bits */
  379. i2c_data = ((val & 0x0300) >> 2) |
  380. (i2c_data & 0x3f);
  381. err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
  382. if (err < 0)
  383. return err;
  384. /* The 8 LSBs */
  385. i2c_data = val & 0xff;
  386. err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
  387. return err;
  388. }
  389. static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
  390. {
  391. int err;
  392. u8 i2c_data;
  393. struct sd *sd = (struct sd *) gspca_dev;
  394. PDEBUG(D_CONF, "Set red gain to %d", val);
  395. i2c_data = val & 0xff;
  396. err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
  397. return err;
  398. }
  399. static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
  400. {
  401. int err;
  402. u8 i2c_data;
  403. struct sd *sd = (struct sd *) gspca_dev;
  404. PDEBUG(D_CONF, "Set blue gain to %d", val);
  405. i2c_data = val & 0xff;
  406. err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
  407. return err;
  408. }
  409. static int ov9650_set_hvflip(struct gspca_dev *gspca_dev)
  410. {
  411. int err;
  412. u8 i2c_data;
  413. struct sd *sd = (struct sd *) gspca_dev;
  414. int hflip = sd->hflip->val;
  415. int vflip = sd->vflip->val;
  416. PDEBUG(D_CONF, "Set hvflip to %d %d", hflip, vflip);
  417. if (dmi_check_system(ov9650_flip_dmi_table))
  418. vflip = !vflip;
  419. i2c_data = (hflip << 5) | (vflip << 4);
  420. err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
  421. if (err < 0)
  422. return err;
  423. /* When vflip is toggled we need to readjust the bridge hsync/vsync */
  424. if (gspca_dev->streaming)
  425. err = ov9650_start(sd);
  426. return err;
  427. }
  428. static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,
  429. __s32 val)
  430. {
  431. int err;
  432. u8 i2c_data;
  433. struct sd *sd = (struct sd *) gspca_dev;
  434. PDEBUG(D_CONF, "Set auto exposure control to %d", val);
  435. err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
  436. if (err < 0)
  437. return err;
  438. val = (val == V4L2_EXPOSURE_AUTO);
  439. i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
  440. return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
  441. }
  442. static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
  443. __s32 val)
  444. {
  445. int err;
  446. u8 i2c_data;
  447. struct sd *sd = (struct sd *) gspca_dev;
  448. PDEBUG(D_CONF, "Set auto white balance to %d", val);
  449. err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
  450. if (err < 0)
  451. return err;
  452. i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
  453. err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
  454. return err;
  455. }
  456. static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
  457. {
  458. int err;
  459. u8 i2c_data;
  460. struct sd *sd = (struct sd *) gspca_dev;
  461. PDEBUG(D_CONF, "Set auto gain control to %d", val);
  462. err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
  463. if (err < 0)
  464. return err;
  465. i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
  466. return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
  467. }
  468. static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl)
  469. {
  470. struct gspca_dev *gspca_dev =
  471. container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
  472. struct sd *sd = (struct sd *) gspca_dev;
  473. int err;
  474. if (!gspca_dev->streaming)
  475. return 0;
  476. switch (ctrl->id) {
  477. case V4L2_CID_AUTO_WHITE_BALANCE:
  478. err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val);
  479. if (err || ctrl->val)
  480. return err;
  481. err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val);
  482. if (err)
  483. return err;
  484. err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val);
  485. break;
  486. case V4L2_CID_EXPOSURE_AUTO:
  487. err = ov9650_set_auto_exposure(gspca_dev, ctrl->val);
  488. if (err || ctrl->val == V4L2_EXPOSURE_AUTO)
  489. return err;
  490. err = ov9650_set_exposure(gspca_dev, sd->expo->val);
  491. break;
  492. case V4L2_CID_AUTOGAIN:
  493. err = ov9650_set_auto_gain(gspca_dev, ctrl->val);
  494. if (err || ctrl->val)
  495. return err;
  496. err = ov9650_set_gain(gspca_dev, sd->gain->val);
  497. break;
  498. case V4L2_CID_HFLIP:
  499. err = ov9650_set_hvflip(gspca_dev);
  500. break;
  501. default:
  502. return -EINVAL;
  503. }
  504. return err;
  505. }
  506. static void ov9650_dump_registers(struct sd *sd)
  507. {
  508. int address;
  509. pr_info("Dumping the ov9650 register state\n");
  510. for (address = 0; address < 0xa9; address++) {
  511. u8 value;
  512. m5602_read_sensor(sd, address, &value, 1);
  513. pr_info("register 0x%x contains 0x%x\n", address, value);
  514. }
  515. pr_info("ov9650 register state dump complete\n");
  516. pr_info("Probing for which registers that are read/write\n");
  517. for (address = 0; address < 0xff; address++) {
  518. u8 old_value, ctrl_value;
  519. u8 test_value[2] = {0xff, 0xff};
  520. m5602_read_sensor(sd, address, &old_value, 1);
  521. m5602_write_sensor(sd, address, test_value, 1);
  522. m5602_read_sensor(sd, address, &ctrl_value, 1);
  523. if (ctrl_value == test_value[0])
  524. pr_info("register 0x%x is writeable\n", address);
  525. else
  526. pr_info("register 0x%x is read only\n", address);
  527. /* Restore original value */
  528. m5602_write_sensor(sd, address, &old_value, 1);
  529. }
  530. }