cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

m5602_s5k83a.c (15649B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for the s5k83a sensor
      4 *
      5 * Copyright (C) 2008 Erik Andrén
      6 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
      7 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
      8 *
      9 * Portions of code to USB interface and ALi driver software,
     10 * Copyright (c) 2006 Willem Duinker
     11 * v4l2 interface modeled after the V4L2 driver
     12 * for SN9C10x PC Camera Controllers
     13 */
     14
     15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     16
     17#include <linux/kthread.h>
     18#include "m5602_s5k83a.h"
     19
     20static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl);
     21
     22static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = {
     23	.s_ctrl = s5k83a_s_ctrl,
     24};
     25
     26static struct v4l2_pix_format s5k83a_modes[] = {
     27	{
     28		640,
     29		480,
     30		V4L2_PIX_FMT_SBGGR8,
     31		V4L2_FIELD_NONE,
     32		.sizeimage =
     33			640 * 480,
     34		.bytesperline = 640,
     35		.colorspace = V4L2_COLORSPACE_SRGB,
     36		.priv = 0
     37	}
     38};
     39
     40static const unsigned char preinit_s5k83a[][4] = {
     41	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
     42	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
     43	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
     44	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
     45	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
     46	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
     47	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
     48
     49	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
     50	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
     51	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
     52	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
     53	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
     54	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
     55	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
     56	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
     57	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
     58	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
     59	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
     60	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
     61	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
     62	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
     63	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
     64	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
     65	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
     66	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
     67	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
     68	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
     69	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
     70	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
     71	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
     72	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
     73	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
     74	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
     75};
     76
     77/* This could probably be considerably shortened.
     78   I don't have the hardware to experiment with it, patches welcome
     79*/
     80static const unsigned char init_s5k83a[][4] = {
     81	/* The following sequence is useless after a clean boot
     82	   but is necessary after resume from suspend */
     83	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
     84	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
     85	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
     86	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
     87	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
     88	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
     89	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
     90	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
     91	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
     92	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
     93	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
     94	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
     95	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
     96	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
     97	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
     98	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
     99	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
    100	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
    101	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
    102	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
    103	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
    104	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
    105	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
    106	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
    107	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
    108
    109	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
    110	{SENSOR, 0xaf, 0x01, 0x00},
    111	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
    112	{SENSOR, 0x7b, 0xff, 0x00},
    113	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
    114	{SENSOR, 0x01, 0x50, 0x00},
    115	{SENSOR, 0x12, 0x20, 0x00},
    116	{SENSOR, 0x17, 0x40, 0x00},
    117	{SENSOR, 0x1c, 0x00, 0x00},
    118	{SENSOR, 0x02, 0x70, 0x00},
    119	{SENSOR, 0x03, 0x0b, 0x00},
    120	{SENSOR, 0x04, 0xf0, 0x00},
    121	{SENSOR, 0x05, 0x0b, 0x00},
    122	{SENSOR, 0x06, 0x71, 0x00},
    123	{SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
    124	{SENSOR, 0x08, 0x02, 0x00},
    125	{SENSOR, 0x09, 0x88, 0x00}, /* 648 */
    126	{SENSOR, 0x14, 0x00, 0x00},
    127	{SENSOR, 0x15, 0x20, 0x00}, /* 32 */
    128	{SENSOR, 0x19, 0x00, 0x00},
    129	{SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
    130	{SENSOR, 0x0f, 0x02, 0x00},
    131	{SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
    132	/* normal colors
    133	(this is value after boot, but after tries can be different) */
    134	{SENSOR, 0x00, 0x06, 0x00},
    135};
    136
    137static const unsigned char start_s5k83a[][4] = {
    138	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
    139	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
    140	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
    141	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
    142	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
    143	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
    144	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
    145	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    146	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    147	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    148	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    149	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
    150	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
    151	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    152	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
    153	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
    154	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
    155	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
    156	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
    157	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
    158	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
    159	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
    160	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
    161	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
    162};
    163
    164static void s5k83a_dump_registers(struct sd *sd);
    165static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
    166static int s5k83a_set_led_indication(struct sd *sd, u8 val);
    167static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
    168				__s32 vflip, __s32 hflip);
    169
    170int s5k83a_probe(struct sd *sd)
    171{
    172	u8 prod_id = 0, ver_id = 0;
    173	int i, err = 0;
    174	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
    175
    176	if (force_sensor) {
    177		if (force_sensor == S5K83A_SENSOR) {
    178			pr_info("Forcing a %s sensor\n", s5k83a.name);
    179			goto sensor_found;
    180		}
    181		/* If we want to force another sensor, don't try to probe this
    182		 * one */
    183		return -ENODEV;
    184	}
    185
    186	gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k83a sensor\n");
    187
    188	/* Preinit the sensor */
    189	for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
    190		u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
    191		if (preinit_s5k83a[i][0] == SENSOR)
    192			err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
    193				data, 2);
    194		else
    195			err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
    196				data[0]);
    197	}
    198
    199	/* We don't know what register (if any) that contain the product id
    200	 * Just pick the first addresses that seem to produce the same results
    201	 * on multiple machines */
    202	if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
    203		return -ENODEV;
    204
    205	if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
    206		return -ENODEV;
    207
    208	if ((prod_id == 0xff) || (ver_id == 0xff))
    209		return -ENODEV;
    210	else
    211		pr_info("Detected a s5k83a sensor\n");
    212
    213sensor_found:
    214	sd->gspca_dev.cam.cam_mode = s5k83a_modes;
    215	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
    216
    217	/* null the pointer! thread is't running now */
    218	sd->rotation_thread = NULL;
    219
    220	return 0;
    221}
    222
    223int s5k83a_init(struct sd *sd)
    224{
    225	int i, err = 0;
    226
    227	for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
    228		u8 data[2] = {0x00, 0x00};
    229
    230		switch (init_s5k83a[i][0]) {
    231		case BRIDGE:
    232			err = m5602_write_bridge(sd,
    233					init_s5k83a[i][1],
    234					init_s5k83a[i][2]);
    235			break;
    236
    237		case SENSOR:
    238			data[0] = init_s5k83a[i][2];
    239			err = m5602_write_sensor(sd,
    240				init_s5k83a[i][1], data, 1);
    241			break;
    242
    243		case SENSOR_LONG:
    244			data[0] = init_s5k83a[i][2];
    245			data[1] = init_s5k83a[i][3];
    246			err = m5602_write_sensor(sd,
    247				init_s5k83a[i][1], data, 2);
    248			break;
    249		default:
    250			pr_info("Invalid stream command, exiting init\n");
    251			return -EINVAL;
    252		}
    253	}
    254
    255	if (dump_sensor)
    256		s5k83a_dump_registers(sd);
    257
    258	return err;
    259}
    260
    261int s5k83a_init_controls(struct sd *sd)
    262{
    263	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
    264
    265	sd->gspca_dev.vdev.ctrl_handler = hdl;
    266	v4l2_ctrl_handler_init(hdl, 6);
    267
    268	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_BRIGHTNESS,
    269			  0, 255, 1, S5K83A_DEFAULT_BRIGHTNESS);
    270
    271	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_EXPOSURE,
    272			  0, S5K83A_MAXIMUM_EXPOSURE, 1,
    273			  S5K83A_DEFAULT_EXPOSURE);
    274
    275	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_GAIN,
    276			  0, 255, 1, S5K83A_DEFAULT_GAIN);
    277
    278	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_HFLIP,
    279				      0, 1, 1, 0);
    280	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_VFLIP,
    281				      0, 1, 1, 0);
    282
    283	if (hdl->error) {
    284		pr_err("Could not initialize controls\n");
    285		return hdl->error;
    286	}
    287
    288	v4l2_ctrl_cluster(2, &sd->hflip);
    289
    290	return 0;
    291}
    292
    293static int rotation_thread_function(void *data)
    294{
    295	struct sd *sd = (struct sd *) data;
    296	u8 reg, previous_rotation = 0;
    297	__s32 vflip, hflip;
    298
    299	set_current_state(TASK_INTERRUPTIBLE);
    300	while (!schedule_timeout(msecs_to_jiffies(100))) {
    301		if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
    302			break;
    303
    304		s5k83a_get_rotation(sd, &reg);
    305		if (previous_rotation != reg) {
    306			previous_rotation = reg;
    307			pr_info("Camera was flipped\n");
    308
    309			hflip = sd->hflip->val;
    310			vflip = sd->vflip->val;
    311
    312			if (reg) {
    313				vflip = !vflip;
    314				hflip = !hflip;
    315			}
    316			s5k83a_set_flip_real((struct gspca_dev *) sd,
    317					      vflip, hflip);
    318		}
    319
    320		mutex_unlock(&sd->gspca_dev.usb_lock);
    321		set_current_state(TASK_INTERRUPTIBLE);
    322	}
    323
    324	/* return to "front" flip */
    325	if (previous_rotation) {
    326		hflip = sd->hflip->val;
    327		vflip = sd->vflip->val;
    328		s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
    329	}
    330
    331	sd->rotation_thread = NULL;
    332	return 0;
    333}
    334
    335int s5k83a_start(struct sd *sd)
    336{
    337	int i, err = 0;
    338
    339	/* Create another thread, polling the GPIO ports of the camera to check
    340	   if it got rotated. This is how the windows driver does it so we have
    341	   to assume that there is no better way of accomplishing this */
    342	sd->rotation_thread = kthread_run(rotation_thread_function,
    343					  sd, "rotation thread");
    344	if (IS_ERR(sd->rotation_thread)) {
    345		err = PTR_ERR(sd->rotation_thread);
    346		sd->rotation_thread = NULL;
    347		return err;
    348	}
    349
    350	/* Preinit the sensor */
    351	for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
    352		u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
    353		if (start_s5k83a[i][0] == SENSOR)
    354			err = m5602_write_sensor(sd, start_s5k83a[i][1],
    355				data, 2);
    356		else
    357			err = m5602_write_bridge(sd, start_s5k83a[i][1],
    358				data[0]);
    359	}
    360	if (err < 0)
    361		return err;
    362
    363	return s5k83a_set_led_indication(sd, 1);
    364}
    365
    366int s5k83a_stop(struct sd *sd)
    367{
    368	if (sd->rotation_thread)
    369		kthread_stop(sd->rotation_thread);
    370
    371	return s5k83a_set_led_indication(sd, 0);
    372}
    373
    374void s5k83a_disconnect(struct sd *sd)
    375{
    376	s5k83a_stop(sd);
    377
    378	sd->sensor = NULL;
    379}
    380
    381static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
    382{
    383	int err;
    384	u8 data[2];
    385	struct sd *sd = (struct sd *) gspca_dev;
    386
    387	data[0] = 0x00;
    388	data[1] = 0x20;
    389	err = m5602_write_sensor(sd, 0x14, data, 2);
    390	if (err < 0)
    391		return err;
    392
    393	data[0] = 0x01;
    394	data[1] = 0x00;
    395	err = m5602_write_sensor(sd, 0x0d, data, 2);
    396	if (err < 0)
    397		return err;
    398
    399	/* FIXME: This is not sane, we need to figure out the composition
    400		  of these registers */
    401	data[0] = val >> 3; /* gain, high 5 bits */
    402	data[1] = val >> 1; /* gain, high 7 bits */
    403	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
    404
    405	return err;
    406}
    407
    408static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
    409{
    410	u8 data[1];
    411	struct sd *sd = (struct sd *) gspca_dev;
    412
    413	data[0] = val;
    414	return m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
    415}
    416
    417static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
    418{
    419	u8 data[2];
    420	struct sd *sd = (struct sd *) gspca_dev;
    421
    422	data[0] = 0;
    423	data[1] = val;
    424	return m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
    425}
    426
    427static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
    428				__s32 vflip, __s32 hflip)
    429{
    430	int err;
    431	u8 data[1];
    432	struct sd *sd = (struct sd *) gspca_dev;
    433
    434	data[0] = 0x05;
    435	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
    436	if (err < 0)
    437		return err;
    438
    439	/* six bit is vflip, seven is hflip */
    440	data[0] = S5K83A_FLIP_MASK;
    441	data[0] = (vflip) ? data[0] | 0x40 : data[0];
    442	data[0] = (hflip) ? data[0] | 0x80 : data[0];
    443
    444	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
    445	if (err < 0)
    446		return err;
    447
    448	data[0] = (vflip) ? 0x0b : 0x0a;
    449	err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
    450	if (err < 0)
    451		return err;
    452
    453	data[0] = (hflip) ? 0x0a : 0x0b;
    454	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
    455	return err;
    456}
    457
    458static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev)
    459{
    460	int err;
    461	u8 reg;
    462	struct sd *sd = (struct sd *) gspca_dev;
    463	int hflip = sd->hflip->val;
    464	int vflip = sd->vflip->val;
    465
    466	err = s5k83a_get_rotation(sd, &reg);
    467	if (err < 0)
    468		return err;
    469	if (reg) {
    470		hflip = !hflip;
    471		vflip = !vflip;
    472	}
    473
    474	err = s5k83a_set_flip_real(gspca_dev, vflip, hflip);
    475	return err;
    476}
    477
    478static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl)
    479{
    480	struct gspca_dev *gspca_dev =
    481		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
    482	int err;
    483
    484	if (!gspca_dev->streaming)
    485		return 0;
    486
    487	switch (ctrl->id) {
    488	case V4L2_CID_BRIGHTNESS:
    489		err = s5k83a_set_brightness(gspca_dev, ctrl->val);
    490		break;
    491	case V4L2_CID_EXPOSURE:
    492		err = s5k83a_set_exposure(gspca_dev, ctrl->val);
    493		break;
    494	case V4L2_CID_GAIN:
    495		err = s5k83a_set_gain(gspca_dev, ctrl->val);
    496		break;
    497	case V4L2_CID_HFLIP:
    498		err = s5k83a_set_hvflip(gspca_dev);
    499		break;
    500	default:
    501		return -EINVAL;
    502	}
    503
    504	return err;
    505}
    506
    507static int s5k83a_set_led_indication(struct sd *sd, u8 val)
    508{
    509	int err = 0;
    510	u8 data[1];
    511
    512	err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
    513	if (err < 0)
    514		return err;
    515
    516	if (val)
    517		data[0] = data[0] | S5K83A_GPIO_LED_MASK;
    518	else
    519		data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
    520
    521	err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
    522
    523	return err;
    524}
    525
    526/* Get camera rotation on Acer notebooks */
    527static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
    528{
    529	int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
    530	*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
    531	return err;
    532}
    533
    534static void s5k83a_dump_registers(struct sd *sd)
    535{
    536	int address;
    537	u8 page, old_page;
    538	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
    539
    540	for (page = 0; page < 16; page++) {
    541		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
    542		pr_info("Dumping the s5k83a register state for page 0x%x\n",
    543			page);
    544		for (address = 0; address <= 0xff; address++) {
    545			u8 val = 0;
    546			m5602_read_sensor(sd, address, &val, 1);
    547			pr_info("register 0x%x contains 0x%x\n", address, val);
    548		}
    549	}
    550	pr_info("s5k83a register state dump complete\n");
    551
    552	for (page = 0; page < 16; page++) {
    553		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
    554		pr_info("Probing for which registers that are read/write for page 0x%x\n",
    555			page);
    556		for (address = 0; address <= 0xff; address++) {
    557			u8 old_val, ctrl_val, test_val = 0xff;
    558
    559			m5602_read_sensor(sd, address, &old_val, 1);
    560			m5602_write_sensor(sd, address, &test_val, 1);
    561			m5602_read_sensor(sd, address, &ctrl_val, 1);
    562
    563			if (ctrl_val == test_val)
    564				pr_info("register 0x%x is writeable\n",
    565					address);
    566			else
    567				pr_info("register 0x%x is read only\n",
    568					address);
    569
    570			/* Restore original val */
    571			m5602_write_sensor(sd, address, &old_val, 1);
    572		}
    573	}
    574	pr_info("Read/write register probing complete\n");
    575	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
    576}