1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * Driver for the s5k83a sensor
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2008 Erik Andrén
60c0d06caSMauro Carvalho Chehab * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
70c0d06caSMauro Carvalho Chehab * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
80c0d06caSMauro Carvalho Chehab *
90c0d06caSMauro Carvalho Chehab * Portions of code to USB interface and ALi driver software,
100c0d06caSMauro Carvalho Chehab * Copyright (c) 2006 Willem Duinker
110c0d06caSMauro Carvalho Chehab * v4l2 interface modeled after the V4L2 driver
120c0d06caSMauro Carvalho Chehab * for SN9C10x PC Camera Controllers
130c0d06caSMauro Carvalho Chehab */
140c0d06caSMauro Carvalho Chehab
150c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
160c0d06caSMauro Carvalho Chehab
170c0d06caSMauro Carvalho Chehab #include <linux/kthread.h>
180c0d06caSMauro Carvalho Chehab #include "m5602_s5k83a.h"
190c0d06caSMauro Carvalho Chehab
20c84e412fSHans de Goede static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl);
21c84e412fSHans de Goede
22c84e412fSHans de Goede static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = {
23c84e412fSHans de Goede .s_ctrl = s5k83a_s_ctrl,
24c84e412fSHans de Goede };
250c0d06caSMauro Carvalho Chehab
260c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format s5k83a_modes[] = {
270c0d06caSMauro Carvalho Chehab {
280c0d06caSMauro Carvalho Chehab 640,
290c0d06caSMauro Carvalho Chehab 480,
300c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8,
310c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE,
320c0d06caSMauro Carvalho Chehab .sizeimage =
330c0d06caSMauro Carvalho Chehab 640 * 480,
340c0d06caSMauro Carvalho Chehab .bytesperline = 640,
350c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB,
360c0d06caSMauro Carvalho Chehab .priv = 0
370c0d06caSMauro Carvalho Chehab }
380c0d06caSMauro Carvalho Chehab };
390c0d06caSMauro Carvalho Chehab
401ab9e600SMauro Carvalho Chehab static const unsigned char preinit_s5k83a[][4] = {
411ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
421ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
431ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
441ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
451ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
461ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
471ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
481ab9e600SMauro Carvalho Chehab
491ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
501ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
511ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
521ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
531ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
541ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
551ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
561ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
571ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
581ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
591ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
601ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
611ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
621ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
631ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
641ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
651ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
661ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
671ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
681ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
691ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
701ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
711ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
721ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
731ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
741ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
751ab9e600SMauro Carvalho Chehab };
761ab9e600SMauro Carvalho Chehab
771ab9e600SMauro Carvalho Chehab /* This could probably be considerably shortened.
781ab9e600SMauro Carvalho Chehab I don't have the hardware to experiment with it, patches welcome
791ab9e600SMauro Carvalho Chehab */
801ab9e600SMauro Carvalho Chehab static const unsigned char init_s5k83a[][4] = {
811ab9e600SMauro Carvalho Chehab /* The following sequence is useless after a clean boot
821ab9e600SMauro Carvalho Chehab but is necessary after resume from suspend */
831ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
841ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
851ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
861ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
871ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
881ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
891ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
901ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
911ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
921ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
931ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
941ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
951ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
961ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
971ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
981ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
991ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
1001ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
1011ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
1021ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
1031ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
1041ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
1051ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
1061ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
1071ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
1081ab9e600SMauro Carvalho Chehab
1091ab9e600SMauro Carvalho Chehab {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
1101ab9e600SMauro Carvalho Chehab {SENSOR, 0xaf, 0x01, 0x00},
1111ab9e600SMauro Carvalho Chehab {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
1121ab9e600SMauro Carvalho Chehab {SENSOR, 0x7b, 0xff, 0x00},
1131ab9e600SMauro Carvalho Chehab {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
1141ab9e600SMauro Carvalho Chehab {SENSOR, 0x01, 0x50, 0x00},
1151ab9e600SMauro Carvalho Chehab {SENSOR, 0x12, 0x20, 0x00},
1161ab9e600SMauro Carvalho Chehab {SENSOR, 0x17, 0x40, 0x00},
1171ab9e600SMauro Carvalho Chehab {SENSOR, 0x1c, 0x00, 0x00},
1181ab9e600SMauro Carvalho Chehab {SENSOR, 0x02, 0x70, 0x00},
1191ab9e600SMauro Carvalho Chehab {SENSOR, 0x03, 0x0b, 0x00},
1201ab9e600SMauro Carvalho Chehab {SENSOR, 0x04, 0xf0, 0x00},
1211ab9e600SMauro Carvalho Chehab {SENSOR, 0x05, 0x0b, 0x00},
1221ab9e600SMauro Carvalho Chehab {SENSOR, 0x06, 0x71, 0x00},
1231ab9e600SMauro Carvalho Chehab {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
1241ab9e600SMauro Carvalho Chehab {SENSOR, 0x08, 0x02, 0x00},
1251ab9e600SMauro Carvalho Chehab {SENSOR, 0x09, 0x88, 0x00}, /* 648 */
1261ab9e600SMauro Carvalho Chehab {SENSOR, 0x14, 0x00, 0x00},
1271ab9e600SMauro Carvalho Chehab {SENSOR, 0x15, 0x20, 0x00}, /* 32 */
1281ab9e600SMauro Carvalho Chehab {SENSOR, 0x19, 0x00, 0x00},
1291ab9e600SMauro Carvalho Chehab {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
1301ab9e600SMauro Carvalho Chehab {SENSOR, 0x0f, 0x02, 0x00},
1311ab9e600SMauro Carvalho Chehab {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
1321ab9e600SMauro Carvalho Chehab /* normal colors
1331ab9e600SMauro Carvalho Chehab (this is value after boot, but after tries can be different) */
1341ab9e600SMauro Carvalho Chehab {SENSOR, 0x00, 0x06, 0x00},
1351ab9e600SMauro Carvalho Chehab };
1361ab9e600SMauro Carvalho Chehab
1371ab9e600SMauro Carvalho Chehab static const unsigned char start_s5k83a[][4] = {
1381ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
1391ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
1401ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
1411ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
1421ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
1431ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
1441ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
1451ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1461ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1471ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1481ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1491ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
1501ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
1511ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1521ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
1531ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1541ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
1551ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1561ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
1571ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
1581ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
1591ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
1601ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
1611ab9e600SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
1621ab9e600SMauro Carvalho Chehab };
1631ab9e600SMauro Carvalho Chehab
1640c0d06caSMauro Carvalho Chehab static void s5k83a_dump_registers(struct sd *sd);
1650c0d06caSMauro Carvalho Chehab static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
1660c0d06caSMauro Carvalho Chehab static int s5k83a_set_led_indication(struct sd *sd, u8 val);
1670c0d06caSMauro Carvalho Chehab static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
1680c0d06caSMauro Carvalho Chehab __s32 vflip, __s32 hflip);
1690c0d06caSMauro Carvalho Chehab
s5k83a_probe(struct sd * sd)1700c0d06caSMauro Carvalho Chehab int s5k83a_probe(struct sd *sd)
1710c0d06caSMauro Carvalho Chehab {
1720c0d06caSMauro Carvalho Chehab u8 prod_id = 0, ver_id = 0;
1730c0d06caSMauro Carvalho Chehab int i, err = 0;
174c93396e1STheodore Kilgore struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
1750c0d06caSMauro Carvalho Chehab
1760c0d06caSMauro Carvalho Chehab if (force_sensor) {
1770c0d06caSMauro Carvalho Chehab if (force_sensor == S5K83A_SENSOR) {
1780c0d06caSMauro Carvalho Chehab pr_info("Forcing a %s sensor\n", s5k83a.name);
1790c0d06caSMauro Carvalho Chehab goto sensor_found;
1800c0d06caSMauro Carvalho Chehab }
1810c0d06caSMauro Carvalho Chehab /* If we want to force another sensor, don't try to probe this
1820c0d06caSMauro Carvalho Chehab * one */
1830c0d06caSMauro Carvalho Chehab return -ENODEV;
1840c0d06caSMauro Carvalho Chehab }
1850c0d06caSMauro Carvalho Chehab
18637d5efb0SJoe Perches gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k83a sensor\n");
1870c0d06caSMauro Carvalho Chehab
1880c0d06caSMauro Carvalho Chehab /* Preinit the sensor */
1890c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
1900c0d06caSMauro Carvalho Chehab u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
1910c0d06caSMauro Carvalho Chehab if (preinit_s5k83a[i][0] == SENSOR)
1920c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
1930c0d06caSMauro Carvalho Chehab data, 2);
1940c0d06caSMauro Carvalho Chehab else
1950c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
1960c0d06caSMauro Carvalho Chehab data[0]);
1970c0d06caSMauro Carvalho Chehab }
1980c0d06caSMauro Carvalho Chehab
1990c0d06caSMauro Carvalho Chehab /* We don't know what register (if any) that contain the product id
2000c0d06caSMauro Carvalho Chehab * Just pick the first addresses that seem to produce the same results
2010c0d06caSMauro Carvalho Chehab * on multiple machines */
2020c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
2030c0d06caSMauro Carvalho Chehab return -ENODEV;
2040c0d06caSMauro Carvalho Chehab
2050c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
2060c0d06caSMauro Carvalho Chehab return -ENODEV;
2070c0d06caSMauro Carvalho Chehab
2080c0d06caSMauro Carvalho Chehab if ((prod_id == 0xff) || (ver_id == 0xff))
2090c0d06caSMauro Carvalho Chehab return -ENODEV;
2100c0d06caSMauro Carvalho Chehab else
2110c0d06caSMauro Carvalho Chehab pr_info("Detected a s5k83a sensor\n");
2120c0d06caSMauro Carvalho Chehab
2130c0d06caSMauro Carvalho Chehab sensor_found:
2140c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.cam_mode = s5k83a_modes;
2150c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
2160c0d06caSMauro Carvalho Chehab
2170c0d06caSMauro Carvalho Chehab /* null the pointer! thread is't running now */
218c84e412fSHans de Goede sd->rotation_thread = NULL;
2190c0d06caSMauro Carvalho Chehab
2200c0d06caSMauro Carvalho Chehab return 0;
2210c0d06caSMauro Carvalho Chehab }
2220c0d06caSMauro Carvalho Chehab
s5k83a_init(struct sd * sd)2230c0d06caSMauro Carvalho Chehab int s5k83a_init(struct sd *sd)
2240c0d06caSMauro Carvalho Chehab {
2250c0d06caSMauro Carvalho Chehab int i, err = 0;
2260c0d06caSMauro Carvalho Chehab
2270c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
2280c0d06caSMauro Carvalho Chehab u8 data[2] = {0x00, 0x00};
2290c0d06caSMauro Carvalho Chehab
2300c0d06caSMauro Carvalho Chehab switch (init_s5k83a[i][0]) {
2310c0d06caSMauro Carvalho Chehab case BRIDGE:
2320c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd,
2330c0d06caSMauro Carvalho Chehab init_s5k83a[i][1],
2340c0d06caSMauro Carvalho Chehab init_s5k83a[i][2]);
2350c0d06caSMauro Carvalho Chehab break;
2360c0d06caSMauro Carvalho Chehab
2370c0d06caSMauro Carvalho Chehab case SENSOR:
2380c0d06caSMauro Carvalho Chehab data[0] = init_s5k83a[i][2];
2390c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd,
2400c0d06caSMauro Carvalho Chehab init_s5k83a[i][1], data, 1);
2410c0d06caSMauro Carvalho Chehab break;
2420c0d06caSMauro Carvalho Chehab
2430c0d06caSMauro Carvalho Chehab case SENSOR_LONG:
2440c0d06caSMauro Carvalho Chehab data[0] = init_s5k83a[i][2];
2450c0d06caSMauro Carvalho Chehab data[1] = init_s5k83a[i][3];
2460c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd,
2470c0d06caSMauro Carvalho Chehab init_s5k83a[i][1], data, 2);
2480c0d06caSMauro Carvalho Chehab break;
2490c0d06caSMauro Carvalho Chehab default:
2500c0d06caSMauro Carvalho Chehab pr_info("Invalid stream command, exiting init\n");
2510c0d06caSMauro Carvalho Chehab return -EINVAL;
2520c0d06caSMauro Carvalho Chehab }
2530c0d06caSMauro Carvalho Chehab }
2540c0d06caSMauro Carvalho Chehab
2550c0d06caSMauro Carvalho Chehab if (dump_sensor)
2560c0d06caSMauro Carvalho Chehab s5k83a_dump_registers(sd);
2570c0d06caSMauro Carvalho Chehab
2580c0d06caSMauro Carvalho Chehab return err;
259c84e412fSHans de Goede }
2600c0d06caSMauro Carvalho Chehab
s5k83a_init_controls(struct sd * sd)261c84e412fSHans de Goede int s5k83a_init_controls(struct sd *sd)
262c84e412fSHans de Goede {
263c84e412fSHans de Goede struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
2640c0d06caSMauro Carvalho Chehab
265c84e412fSHans de Goede sd->gspca_dev.vdev.ctrl_handler = hdl;
266c84e412fSHans de Goede v4l2_ctrl_handler_init(hdl, 6);
2670c0d06caSMauro Carvalho Chehab
268c84e412fSHans de Goede v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_BRIGHTNESS,
269c84e412fSHans de Goede 0, 255, 1, S5K83A_DEFAULT_BRIGHTNESS);
2700c0d06caSMauro Carvalho Chehab
271c84e412fSHans de Goede v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_EXPOSURE,
272c84e412fSHans de Goede 0, S5K83A_MAXIMUM_EXPOSURE, 1,
273c84e412fSHans de Goede S5K83A_DEFAULT_EXPOSURE);
2740c0d06caSMauro Carvalho Chehab
275c84e412fSHans de Goede v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_GAIN,
276c84e412fSHans de Goede 0, 255, 1, S5K83A_DEFAULT_GAIN);
277c84e412fSHans de Goede
278c84e412fSHans de Goede sd->hflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_HFLIP,
279c84e412fSHans de Goede 0, 1, 1, 0);
280c84e412fSHans de Goede sd->vflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_VFLIP,
281c84e412fSHans de Goede 0, 1, 1, 0);
282c84e412fSHans de Goede
283c84e412fSHans de Goede if (hdl->error) {
284c84e412fSHans de Goede pr_err("Could not initialize controls\n");
285c84e412fSHans de Goede return hdl->error;
286c84e412fSHans de Goede }
287c84e412fSHans de Goede
288c84e412fSHans de Goede v4l2_ctrl_cluster(2, &sd->hflip);
289c84e412fSHans de Goede
290c84e412fSHans de Goede return 0;
2910c0d06caSMauro Carvalho Chehab }
2920c0d06caSMauro Carvalho Chehab
rotation_thread_function(void * data)2930c0d06caSMauro Carvalho Chehab static int rotation_thread_function(void *data)
2940c0d06caSMauro Carvalho Chehab {
2950c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) data;
2960c0d06caSMauro Carvalho Chehab u8 reg, previous_rotation = 0;
2970c0d06caSMauro Carvalho Chehab __s32 vflip, hflip;
2980c0d06caSMauro Carvalho Chehab
2990c0d06caSMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE);
30063f2f417SNicholas Mc Guire while (!schedule_timeout(msecs_to_jiffies(100))) {
3010c0d06caSMauro Carvalho Chehab if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
3020c0d06caSMauro Carvalho Chehab break;
3030c0d06caSMauro Carvalho Chehab
3040c0d06caSMauro Carvalho Chehab s5k83a_get_rotation(sd, ®);
3050c0d06caSMauro Carvalho Chehab if (previous_rotation != reg) {
3060c0d06caSMauro Carvalho Chehab previous_rotation = reg;
3070c0d06caSMauro Carvalho Chehab pr_info("Camera was flipped\n");
3080c0d06caSMauro Carvalho Chehab
309c84e412fSHans de Goede hflip = sd->hflip->val;
310c84e412fSHans de Goede vflip = sd->vflip->val;
3110c0d06caSMauro Carvalho Chehab
3120c0d06caSMauro Carvalho Chehab if (reg) {
3130c0d06caSMauro Carvalho Chehab vflip = !vflip;
3140c0d06caSMauro Carvalho Chehab hflip = !hflip;
3150c0d06caSMauro Carvalho Chehab }
3160c0d06caSMauro Carvalho Chehab s5k83a_set_flip_real((struct gspca_dev *) sd,
3170c0d06caSMauro Carvalho Chehab vflip, hflip);
3180c0d06caSMauro Carvalho Chehab }
3190c0d06caSMauro Carvalho Chehab
3200c0d06caSMauro Carvalho Chehab mutex_unlock(&sd->gspca_dev.usb_lock);
3210c0d06caSMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE);
3220c0d06caSMauro Carvalho Chehab }
3230c0d06caSMauro Carvalho Chehab
3240c0d06caSMauro Carvalho Chehab /* return to "front" flip */
3250c0d06caSMauro Carvalho Chehab if (previous_rotation) {
326c84e412fSHans de Goede hflip = sd->hflip->val;
327c84e412fSHans de Goede vflip = sd->vflip->val;
3280c0d06caSMauro Carvalho Chehab s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
3290c0d06caSMauro Carvalho Chehab }
3300c0d06caSMauro Carvalho Chehab
331c84e412fSHans de Goede sd->rotation_thread = NULL;
3320c0d06caSMauro Carvalho Chehab return 0;
3330c0d06caSMauro Carvalho Chehab }
3340c0d06caSMauro Carvalho Chehab
s5k83a_start(struct sd * sd)3350c0d06caSMauro Carvalho Chehab int s5k83a_start(struct sd *sd)
3360c0d06caSMauro Carvalho Chehab {
3370c0d06caSMauro Carvalho Chehab int i, err = 0;
3380c0d06caSMauro Carvalho Chehab
3390c0d06caSMauro Carvalho Chehab /* Create another thread, polling the GPIO ports of the camera to check
3400c0d06caSMauro Carvalho Chehab if it got rotated. This is how the windows driver does it so we have
3410c0d06caSMauro Carvalho Chehab to assume that there is no better way of accomplishing this */
34220c82fffSCai Huoqing sd->rotation_thread = kthread_run(rotation_thread_function,
3430c0d06caSMauro Carvalho Chehab sd, "rotation thread");
34413174c38SPan Bian if (IS_ERR(sd->rotation_thread)) {
34513174c38SPan Bian err = PTR_ERR(sd->rotation_thread);
34613174c38SPan Bian sd->rotation_thread = NULL;
34713174c38SPan Bian return err;
34813174c38SPan Bian }
3490c0d06caSMauro Carvalho Chehab
3500c0d06caSMauro Carvalho Chehab /* Preinit the sensor */
3510c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
3520c0d06caSMauro Carvalho Chehab u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
3530c0d06caSMauro Carvalho Chehab if (start_s5k83a[i][0] == SENSOR)
3540c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, start_s5k83a[i][1],
3550c0d06caSMauro Carvalho Chehab data, 2);
3560c0d06caSMauro Carvalho Chehab else
3570c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, start_s5k83a[i][1],
3580c0d06caSMauro Carvalho Chehab data[0]);
3590c0d06caSMauro Carvalho Chehab }
3600c0d06caSMauro Carvalho Chehab if (err < 0)
3610c0d06caSMauro Carvalho Chehab return err;
3620c0d06caSMauro Carvalho Chehab
3630c0d06caSMauro Carvalho Chehab return s5k83a_set_led_indication(sd, 1);
3640c0d06caSMauro Carvalho Chehab }
3650c0d06caSMauro Carvalho Chehab
s5k83a_stop(struct sd * sd)3660c0d06caSMauro Carvalho Chehab int s5k83a_stop(struct sd *sd)
3670c0d06caSMauro Carvalho Chehab {
368c84e412fSHans de Goede if (sd->rotation_thread)
369c84e412fSHans de Goede kthread_stop(sd->rotation_thread);
3700c0d06caSMauro Carvalho Chehab
3710c0d06caSMauro Carvalho Chehab return s5k83a_set_led_indication(sd, 0);
3720c0d06caSMauro Carvalho Chehab }
3730c0d06caSMauro Carvalho Chehab
s5k83a_disconnect(struct sd * sd)3740c0d06caSMauro Carvalho Chehab void s5k83a_disconnect(struct sd *sd)
3750c0d06caSMauro Carvalho Chehab {
3760c0d06caSMauro Carvalho Chehab s5k83a_stop(sd);
3770c0d06caSMauro Carvalho Chehab
3780c0d06caSMauro Carvalho Chehab sd->sensor = NULL;
3790c0d06caSMauro Carvalho Chehab }
3800c0d06caSMauro Carvalho Chehab
s5k83a_set_gain(struct gspca_dev * gspca_dev,__s32 val)3810c0d06caSMauro Carvalho Chehab static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
3820c0d06caSMauro Carvalho Chehab {
3830c0d06caSMauro Carvalho Chehab int err;
3840c0d06caSMauro Carvalho Chehab u8 data[2];
3850c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev;
3860c0d06caSMauro Carvalho Chehab
3870c0d06caSMauro Carvalho Chehab data[0] = 0x00;
3880c0d06caSMauro Carvalho Chehab data[1] = 0x20;
3890c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 0x14, data, 2);
3900c0d06caSMauro Carvalho Chehab if (err < 0)
3910c0d06caSMauro Carvalho Chehab return err;
3920c0d06caSMauro Carvalho Chehab
3930c0d06caSMauro Carvalho Chehab data[0] = 0x01;
3940c0d06caSMauro Carvalho Chehab data[1] = 0x00;
3950c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 0x0d, data, 2);
3960c0d06caSMauro Carvalho Chehab if (err < 0)
3970c0d06caSMauro Carvalho Chehab return err;
3980c0d06caSMauro Carvalho Chehab
3990c0d06caSMauro Carvalho Chehab /* FIXME: This is not sane, we need to figure out the composition
4000c0d06caSMauro Carvalho Chehab of these registers */
4010c0d06caSMauro Carvalho Chehab data[0] = val >> 3; /* gain, high 5 bits */
4020c0d06caSMauro Carvalho Chehab data[1] = val >> 1; /* gain, high 7 bits */
4030c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
4040c0d06caSMauro Carvalho Chehab
4050c0d06caSMauro Carvalho Chehab return err;
4060c0d06caSMauro Carvalho Chehab }
4070c0d06caSMauro Carvalho Chehab
s5k83a_set_brightness(struct gspca_dev * gspca_dev,__s32 val)4080c0d06caSMauro Carvalho Chehab static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
4090c0d06caSMauro Carvalho Chehab {
4100c0d06caSMauro Carvalho Chehab u8 data[1];
4110c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev;
4120c0d06caSMauro Carvalho Chehab
4130c0d06caSMauro Carvalho Chehab data[0] = val;
414*5d6db4aaSchiminghao return m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
4150c0d06caSMauro Carvalho Chehab }
4160c0d06caSMauro Carvalho Chehab
s5k83a_set_exposure(struct gspca_dev * gspca_dev,__s32 val)4170c0d06caSMauro Carvalho Chehab static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
4180c0d06caSMauro Carvalho Chehab {
4190c0d06caSMauro Carvalho Chehab u8 data[2];
4200c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev;
4210c0d06caSMauro Carvalho Chehab
4220c0d06caSMauro Carvalho Chehab data[0] = 0;
4230c0d06caSMauro Carvalho Chehab data[1] = val;
424*5d6db4aaSchiminghao return m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
4250c0d06caSMauro Carvalho Chehab }
4260c0d06caSMauro Carvalho Chehab
s5k83a_set_flip_real(struct gspca_dev * gspca_dev,__s32 vflip,__s32 hflip)4270c0d06caSMauro Carvalho Chehab static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
4280c0d06caSMauro Carvalho Chehab __s32 vflip, __s32 hflip)
4290c0d06caSMauro Carvalho Chehab {
4300c0d06caSMauro Carvalho Chehab int err;
4310c0d06caSMauro Carvalho Chehab u8 data[1];
4320c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev;
4330c0d06caSMauro Carvalho Chehab
4340c0d06caSMauro Carvalho Chehab data[0] = 0x05;
4350c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
4360c0d06caSMauro Carvalho Chehab if (err < 0)
4370c0d06caSMauro Carvalho Chehab return err;
4380c0d06caSMauro Carvalho Chehab
4390c0d06caSMauro Carvalho Chehab /* six bit is vflip, seven is hflip */
4400c0d06caSMauro Carvalho Chehab data[0] = S5K83A_FLIP_MASK;
4410c0d06caSMauro Carvalho Chehab data[0] = (vflip) ? data[0] | 0x40 : data[0];
4420c0d06caSMauro Carvalho Chehab data[0] = (hflip) ? data[0] | 0x80 : data[0];
4430c0d06caSMauro Carvalho Chehab
4440c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
4450c0d06caSMauro Carvalho Chehab if (err < 0)
4460c0d06caSMauro Carvalho Chehab return err;
4470c0d06caSMauro Carvalho Chehab
4480c0d06caSMauro Carvalho Chehab data[0] = (vflip) ? 0x0b : 0x0a;
4490c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
4500c0d06caSMauro Carvalho Chehab if (err < 0)
4510c0d06caSMauro Carvalho Chehab return err;
4520c0d06caSMauro Carvalho Chehab
4530c0d06caSMauro Carvalho Chehab data[0] = (hflip) ? 0x0a : 0x0b;
4540c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
4550c0d06caSMauro Carvalho Chehab return err;
4560c0d06caSMauro Carvalho Chehab }
4570c0d06caSMauro Carvalho Chehab
s5k83a_set_hvflip(struct gspca_dev * gspca_dev)458c84e412fSHans de Goede static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev)
4590c0d06caSMauro Carvalho Chehab {
4600c0d06caSMauro Carvalho Chehab int err;
4610c0d06caSMauro Carvalho Chehab u8 reg;
4620c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev;
463c84e412fSHans de Goede int hflip = sd->hflip->val;
464c84e412fSHans de Goede int vflip = sd->vflip->val;
4650c0d06caSMauro Carvalho Chehab
4660c0d06caSMauro Carvalho Chehab err = s5k83a_get_rotation(sd, ®);
4670c0d06caSMauro Carvalho Chehab if (err < 0)
4680c0d06caSMauro Carvalho Chehab return err;
4690c0d06caSMauro Carvalho Chehab if (reg) {
4700c0d06caSMauro Carvalho Chehab hflip = !hflip;
4710c0d06caSMauro Carvalho Chehab vflip = !vflip;
4720c0d06caSMauro Carvalho Chehab }
4730c0d06caSMauro Carvalho Chehab
474c84e412fSHans de Goede err = s5k83a_set_flip_real(gspca_dev, vflip, hflip);
475c84e412fSHans de Goede return err;
476c84e412fSHans de Goede }
477c84e412fSHans de Goede
s5k83a_s_ctrl(struct v4l2_ctrl * ctrl)478c84e412fSHans de Goede static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl)
479c84e412fSHans de Goede {
480c84e412fSHans de Goede struct gspca_dev *gspca_dev =
481c84e412fSHans de Goede container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
482c84e412fSHans de Goede int err;
483c84e412fSHans de Goede
484c84e412fSHans de Goede if (!gspca_dev->streaming)
485c84e412fSHans de Goede return 0;
486c84e412fSHans de Goede
487c84e412fSHans de Goede switch (ctrl->id) {
488c84e412fSHans de Goede case V4L2_CID_BRIGHTNESS:
489c84e412fSHans de Goede err = s5k83a_set_brightness(gspca_dev, ctrl->val);
490c84e412fSHans de Goede break;
491c84e412fSHans de Goede case V4L2_CID_EXPOSURE:
492c84e412fSHans de Goede err = s5k83a_set_exposure(gspca_dev, ctrl->val);
493c84e412fSHans de Goede break;
494c84e412fSHans de Goede case V4L2_CID_GAIN:
495c84e412fSHans de Goede err = s5k83a_set_gain(gspca_dev, ctrl->val);
496c84e412fSHans de Goede break;
497c84e412fSHans de Goede case V4L2_CID_HFLIP:
498c84e412fSHans de Goede err = s5k83a_set_hvflip(gspca_dev);
499c84e412fSHans de Goede break;
500c84e412fSHans de Goede default:
501c84e412fSHans de Goede return -EINVAL;
502c84e412fSHans de Goede }
503c84e412fSHans de Goede
5040c0d06caSMauro Carvalho Chehab return err;
5050c0d06caSMauro Carvalho Chehab }
5060c0d06caSMauro Carvalho Chehab
s5k83a_set_led_indication(struct sd * sd,u8 val)5070c0d06caSMauro Carvalho Chehab static int s5k83a_set_led_indication(struct sd *sd, u8 val)
5080c0d06caSMauro Carvalho Chehab {
5090c0d06caSMauro Carvalho Chehab int err = 0;
5100c0d06caSMauro Carvalho Chehab u8 data[1];
5110c0d06caSMauro Carvalho Chehab
5120c0d06caSMauro Carvalho Chehab err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
5130c0d06caSMauro Carvalho Chehab if (err < 0)
5140c0d06caSMauro Carvalho Chehab return err;
5150c0d06caSMauro Carvalho Chehab
5160c0d06caSMauro Carvalho Chehab if (val)
5170c0d06caSMauro Carvalho Chehab data[0] = data[0] | S5K83A_GPIO_LED_MASK;
5180c0d06caSMauro Carvalho Chehab else
5190c0d06caSMauro Carvalho Chehab data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
5200c0d06caSMauro Carvalho Chehab
5210c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
5220c0d06caSMauro Carvalho Chehab
5230c0d06caSMauro Carvalho Chehab return err;
5240c0d06caSMauro Carvalho Chehab }
5250c0d06caSMauro Carvalho Chehab
5260c0d06caSMauro Carvalho Chehab /* Get camera rotation on Acer notebooks */
s5k83a_get_rotation(struct sd * sd,u8 * reg_data)5270c0d06caSMauro Carvalho Chehab static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
5280c0d06caSMauro Carvalho Chehab {
5290c0d06caSMauro Carvalho Chehab int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
5300c0d06caSMauro Carvalho Chehab *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
5310c0d06caSMauro Carvalho Chehab return err;
5320c0d06caSMauro Carvalho Chehab }
5330c0d06caSMauro Carvalho Chehab
s5k83a_dump_registers(struct sd * sd)5340c0d06caSMauro Carvalho Chehab static void s5k83a_dump_registers(struct sd *sd)
5350c0d06caSMauro Carvalho Chehab {
5360c0d06caSMauro Carvalho Chehab int address;
5370c0d06caSMauro Carvalho Chehab u8 page, old_page;
5380c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
5390c0d06caSMauro Carvalho Chehab
5400c0d06caSMauro Carvalho Chehab for (page = 0; page < 16; page++) {
5410c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
5420c0d06caSMauro Carvalho Chehab pr_info("Dumping the s5k83a register state for page 0x%x\n",
5430c0d06caSMauro Carvalho Chehab page);
5440c0d06caSMauro Carvalho Chehab for (address = 0; address <= 0xff; address++) {
5450c0d06caSMauro Carvalho Chehab u8 val = 0;
5460c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &val, 1);
5470c0d06caSMauro Carvalho Chehab pr_info("register 0x%x contains 0x%x\n", address, val);
5480c0d06caSMauro Carvalho Chehab }
5490c0d06caSMauro Carvalho Chehab }
5500c0d06caSMauro Carvalho Chehab pr_info("s5k83a register state dump complete\n");
5510c0d06caSMauro Carvalho Chehab
5520c0d06caSMauro Carvalho Chehab for (page = 0; page < 16; page++) {
5530c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
5540c0d06caSMauro Carvalho Chehab pr_info("Probing for which registers that are read/write for page 0x%x\n",
5550c0d06caSMauro Carvalho Chehab page);
5560c0d06caSMauro Carvalho Chehab for (address = 0; address <= 0xff; address++) {
5570c0d06caSMauro Carvalho Chehab u8 old_val, ctrl_val, test_val = 0xff;
5580c0d06caSMauro Carvalho Chehab
5590c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &old_val, 1);
5600c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, &test_val, 1);
5610c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &ctrl_val, 1);
5620c0d06caSMauro Carvalho Chehab
5630c0d06caSMauro Carvalho Chehab if (ctrl_val == test_val)
5640c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is writeable\n",
5650c0d06caSMauro Carvalho Chehab address);
5660c0d06caSMauro Carvalho Chehab else
5670c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is read only\n",
5680c0d06caSMauro Carvalho Chehab address);
5690c0d06caSMauro Carvalho Chehab
5700c0d06caSMauro Carvalho Chehab /* Restore original val */
5710c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, &old_val, 1);
5720c0d06caSMauro Carvalho Chehab }
5730c0d06caSMauro Carvalho Chehab }
5740c0d06caSMauro Carvalho Chehab pr_info("Read/write register probing complete\n");
5750c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
5760c0d06caSMauro Carvalho Chehab }
577