Commit ac3d5bfe authored by Luk?? Karas's avatar Luk?? Karas Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (11451): gspca - m5602-s5k83a: Add rotation, ctrl cache. Rename some ctrls.



s5k83a sensor mounted on many acer laptops have a swiwel allowing it to be rotated. When the camera is in its rotated state, the image needs to be flipped. The only way to check for if the camera has been flipped is to continously poll a register in the m5602. This patch creates a kernel thread which does this. This patch renames some v4l2 ctrls and finally implements a cache in order to prevent unnecessary sensor reads.

Signed-off-by: default avatarLuk?? Karas <lukas.karas@centrum.cz>
Signed-off-by: default avatarErik Andr?n <erik.andren@gmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 04f15655
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -80,6 +80,16 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
	return (err < 0) ? err : 0;
}

int m5602_wait_for_i2c(struct sd *sd)
{
	int err;
	u8 data;
	do {
		err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data);
	} while ((data & I2C_BUSY) && !err);
	return (err < 0) ? err : 0;
}

int m5602_read_sensor(struct sd *sd, const u8 address,
		       u8 *i2c_data, const u8 len)
{
@@ -88,9 +98,7 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
	if (!len || len > sd->sensor->i2c_regW)
		return -EINVAL;

	do {
		err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
	} while ((*i2c_data & I2C_BUSY) && !err);
	err = m5602_wait_for_i2c(sd);
	if (err < 0)
		return err;

@@ -118,6 +126,10 @@ int m5602_read_sensor(struct sd *sd, const u8 address,
	}

	for (i = 0; (i < len) && !err; i++) {
		err = m5602_wait_for_i2c(sd);
		if (err < 0)
			return err;

		err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));

		PDEBUG(D_CONF, "Reading sensor register "
+242 −147
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
 *
 */

#include <linux/kthread.h>
#include "m5602_s5k83a.h"

static struct v4l2_pix_format s5k83a_modes[] = {
@@ -33,47 +34,54 @@ static struct v4l2_pix_format s5k83a_modes[] = {
};

const static struct ctrl s5k83a_ctrls[] = {
#define GAIN_IDX 0
	{
		{
			.id = V4L2_CID_BRIGHTNESS,
			.id = V4L2_CID_GAIN,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "brightness",
			.name = "gain",
			.minimum = 0x00,
			.maximum = 0xff,
			.step = 0x01,
			.default_value = S5K83A_DEFAULT_BRIGHTNESS,
			.default_value = S5K83A_DEFAULT_GAIN,
			.flags = V4L2_CTRL_FLAG_SLIDER
		},
			.set = s5k83a_set_brightness,
			.get = s5k83a_get_brightness
			.set = s5k83a_set_gain,
			.get = s5k83a_get_gain

	}, {
	},
#define BRIGHTNESS_IDX 1
	{
		{
			.id = V4L2_CID_WHITENESS,
			.id = V4L2_CID_BRIGHTNESS,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "whiteness",
			.name = "brightness",
			.minimum = 0x00,
			.maximum = 0xff,
			.step = 0x01,
			.default_value = S5K83A_DEFAULT_WHITENESS,
			.default_value = S5K83A_DEFAULT_BRIGHTNESS,
			.flags = V4L2_CTRL_FLAG_SLIDER
		},
			.set = s5k83a_set_whiteness,
			.get = s5k83a_get_whiteness,
	}, {
			.set = s5k83a_set_brightness,
			.get = s5k83a_get_brightness,
	},
#define EXPOSURE_IDX 2
	{
			.id = V4L2_CID_GAIN,
		{
			.id = V4L2_CID_EXPOSURE,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "gain",
			.name = "exposure",
			.minimum = 0x00,
			.maximum = S5K83A_MAXIMUM_GAIN,
			.maximum = S5K83A_MAXIMUM_EXPOSURE,
			.step = 0x01,
			.default_value = S5K83A_DEFAULT_GAIN,
			.default_value = S5K83A_DEFAULT_EXPOSURE,
			.flags = V4L2_CTRL_FLAG_SLIDER
		},
			.set = s5k83a_set_gain,
			.get = s5k83a_get_gain
	}, {
			.set = s5k83a_set_exposure,
			.get = s5k83a_get_exposure
	},
#define HFLIP_IDX 3
	{
		{
			.id         = V4L2_CID_HFLIP,
			.type       = V4L2_CTRL_TYPE_BOOLEAN,
@@ -85,7 +93,9 @@ const static struct ctrl s5k83a_ctrls[] = {
		},
			.set = s5k83a_set_hflip,
			.get = s5k83a_get_hflip
	}, {
	},
#define VFLIP_IDX 4
	{
		{
		 .id         = V4L2_CID_VFLIP,
		.type       = V4L2_CTRL_TYPE_BOOLEAN,
@@ -101,9 +111,13 @@ const static struct ctrl s5k83a_ctrls[] = {
};

static void s5k83a_dump_registers(struct sd *sd);
static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
static int s5k83a_set_led_indication(struct sd *sd, u8 val);
int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, __s32 vflip, __s32 hflip);

int s5k83a_probe(struct sd *sd)
{
	struct s5k83a_priv *sens_priv;
	u8 prod_id = 0, ver_id = 0;
	int i, err = 0;

@@ -145,10 +159,28 @@ int s5k83a_probe(struct sd *sd)
		info("Detected a s5k83a sensor");

sensor_found:
	sens_priv = kmalloc(
		sizeof(struct s5k83a_priv), GFP_KERNEL);
	if (!sens_priv)
		return -ENOMEM;

	sens_priv->settings =
	kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
	if (!sens_priv->settings)
		return -ENOMEM;

	sd->gspca_dev.cam.cam_mode = s5k83a_modes;
	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
	sd->desc->ctrls = s5k83a_ctrls;
	sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls);

	/* null the pointer! thread is't running now */
	sens_priv->rotation_thread = NULL;

	for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++)
		sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value;

	sd->sensor_priv = sens_priv;
	return 0;
}

@@ -190,84 +222,104 @@ int s5k83a_init(struct sd *sd)
	return (err < 0) ? err : 0;
}

int s5k83a_start(struct sd *sd)
static int rotation_thread_function(void *data)
{
	return s5k83a_set_led_indication(sd, 1);
	struct sd *sd = (struct sd *) data;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;
	u8 reg, previous_rotation = 0;
	__s32 vflip, hflip;

	set_current_state(TASK_INTERRUPTIBLE);
	while (!schedule_timeout(100)) {
		if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
			break;

		s5k83a_get_rotation(sd, &reg);
		if (previous_rotation != reg) {
			previous_rotation = reg;
			info("Camera was flipped");

			s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
			s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);

			if (reg) {
				vflip = !vflip;
				hflip = !hflip;
			}
			s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
		}

int s5k83a_stop(struct sd *sd)
{
	return s5k83a_set_led_indication(sd, 0);
		mutex_unlock(&sd->gspca_dev.usb_lock);
		set_current_state(TASK_INTERRUPTIBLE);
	}

int s5k83a_power_down(struct sd *sd)
{
	/* return to "front" flip */
	if (previous_rotation) {
		s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
		s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
		s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
	}

	sens_priv->rotation_thread = NULL;
	return 0;
}

static void s5k83a_dump_registers(struct sd *sd)
int s5k83a_start(struct sd *sd)
{
	int address;
	u8 page, old_page;
	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	for (page = 0; page < 16; page++) {
		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
		info("Dumping the s5k83a register state for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 val = 0;
			m5602_read_sensor(sd, address, &val, 1);
			info("register 0x%x contains 0x%x",
			     address, val);
		}
	}
	info("s5k83a register state dump complete");
	/* Create another thread, polling the GPIO ports of the camera to check
	   if it got rotated. This is how the windows driver does it so we have
	   to assume that there is no better way of accomplishing this */
	sens_priv->rotation_thread = kthread_create(rotation_thread_function, sd, "rotation thread");
	wake_up_process(sens_priv->rotation_thread);

	for (page = 0; page < 16; page++) {
		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
		info("Probing for which registers that are read/write "
		      "for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 old_val, ctrl_val, test_val = 0xff;
	return s5k83a_set_led_indication(sd, 1);
}

			m5602_read_sensor(sd, address, &old_val, 1);
			m5602_write_sensor(sd, address, &test_val, 1);
			m5602_read_sensor(sd, address, &ctrl_val, 1);
int s5k83a_stop(struct sd *sd)
{
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

			if (ctrl_val == test_val)
				info("register 0x%x is writeable", address);
			else
				info("register 0x%x is read only", address);
	if (sens_priv->rotation_thread)
		kthread_stop(sens_priv->rotation_thread);

			/* Restore original val */
			m5602_write_sensor(sd, address, &old_val, 1);
	return s5k83a_set_led_indication(sd, 0);
}

void s5k83a_disconnect(struct sd *sd)
{
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	s5k83a_stop(sd);

	sd->sensor = NULL;
	kfree(sens_priv->settings);
	kfree(sens_priv);
}
	info("Read/write register probing complete");
	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);

int s5k83a_power_down(struct sd *sd)
{
	return 0;
}

int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
	int err;
	u8 data[2];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
	if (err < 0)
		return err;

	data[1] = data[1] << 1;
	*val = data[1];

	return err;
	*val = sens_priv->settings[GAIN_IDX];
	return 0;
}

int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
{
	int err;
	u8 data[2];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	sens_priv->settings[GAIN_IDX] = val;

	data[0] = 0x00;
	data[1] = 0x20;
@@ -283,89 +335,68 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)

	/* FIXME: This is not sane, we need to figure out the composition
		  of these registers */
	data[0] = val >> 3; /* brightness, high 5 bits */
	data[1] = val >> 1; /* brightness, high 7 bits */
	err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
	data[0] = val >> 3; /* gain, high 5 bits */
	data[1] = val >> 1; /* gain, high 7 bits */
	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);

	return err;
}

int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{
	int err;
	u8 data;
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
	if (err < 0)
		return err;

	*val = data;

	return err;
	*val = sens_priv->settings[BRIGHTNESS_IDX];
	return 0;
}

int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{
	int err;
	u8 data[1];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	sens_priv->settings[BRIGHTNESS_IDX] = val;
	data[0] = val;
	err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1);

	err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
	return err;
}

int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
	int err;
	u8 data[2];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2);
	if (err < 0)
		return err;

	data[1] = data[1] & 0x3f;
	if (data[1] > S5K83A_MAXIMUM_GAIN)
		data[1] = S5K83A_MAXIMUM_GAIN;

	*val = data[1];

	return err;
	*val = sens_priv->settings[EXPOSURE_IDX];
	return 0;
}

int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
	int err;
	u8 data[2];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	sens_priv->settings[EXPOSURE_IDX] = val;
	data[0] = 0;
	data[1] = val;
	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
	err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
	return err;
}

int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
	int err;
	u8 data[1];
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	data[0] = 0x05;
	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
	if (err < 0)
		return err;

	err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
	*val = (data[0] | 0x40) ? 1 : 0;

	return err;
	*val = sens_priv->settings[VFLIP_IDX];
	return 0;
}

int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, __s32 vflip, __s32 hflip)
{
	int err;
	u8 data[1];
@@ -376,69 +407,83 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
	if (err < 0)
		return err;

	err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
	if (err < 0)
		return err;
	/* six bit is vflip, seven is hflip */
	data[0] = S5K83A_FLIP_MASK;
	data[0] = (vflip) ? data[0] | 0x40 : data[0];
	data[0] = (hflip) ? data[0] | 0x80 : data[0];

	/* set or zero six bit, seven is hflip */
	data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
			: (data[0] & 0x80) | S5K83A_FLIP_MASK;
	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
	if (err < 0)
		return err;

	data[0] = (val) ? 0x0b : 0x0a;
	data[0] = (vflip) ? 0x0b : 0x0a;
	err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
	if (err < 0)
		return err;

	data[0] = (hflip) ? 0x0a : 0x0b;
	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
	return err;
}

int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
	int err;
	u8 data[1];
	u8 reg;
	__s32 hflip;
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	data[0] = 0x05;
	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
	sens_priv->settings[VFLIP_IDX] = val;

	s5k83a_get_hflip(gspca_dev, &hflip);

	err = s5k83a_get_rotation(sd, &reg);
	if (err < 0)
		return err;
	if (reg) {
		val = !val;
		hflip = !hflip;
	}

	err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
	*val = (data[0] | 0x80) ? 1 : 0;

	err = s5k83a_set_flip_real(gspca_dev, val, hflip);
	return err;
}

int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	*val = sens_priv->settings[HFLIP_IDX];
	return 0;
}

int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
	int err;
	u8 data[1];
	u8 reg;
	__s32 vflip;
	struct sd *sd = (struct sd *) gspca_dev;
	struct s5k83a_priv *sens_priv = sd->sensor_priv;

	data[0] = 0x05;
	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
	if (err < 0)
		return err;
	sens_priv->settings[HFLIP_IDX] = val;

	err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
	if (err < 0)
		return err;
	s5k83a_get_vflip(gspca_dev, &vflip);

	/* set or zero seven bit, six is vflip */
	data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
			: (data[0] & 0x40) | S5K83A_FLIP_MASK;
	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
	err = s5k83a_get_rotation(sd, &reg);
	if (err < 0)
		return err;
	if (reg) {
		val = !val;
		vflip = !vflip;
	}

	data[0] = (val) ? 0x0a : 0x0b;
	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);

	err = s5k83a_set_flip_real(gspca_dev, vflip, val);
	return err;
}

int s5k83a_set_led_indication(struct sd *sd, u8 val)
static int s5k83a_set_led_indication(struct sd *sd, u8 val)
{
	int err = 0;
	u8 data[1];
@@ -456,3 +501,53 @@ int s5k83a_set_led_indication(struct sd *sd, u8 val)

	return (err < 0) ? err : 0;
}

/* Get camera rotation on Acer notebooks */
static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
{
	int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
	*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
	return err;
}

static void s5k83a_dump_registers(struct sd *sd)
{
	int address;
	u8 page, old_page;
	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);

	for (page = 0; page < 16; page++) {
		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
		info("Dumping the s5k83a register state for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 val = 0;
			m5602_read_sensor(sd, address, &val, 1);
			info("register 0x%x contains 0x%x",
			     address, val);
		}
	}
	info("s5k83a register state dump complete");

	for (page = 0; page < 16; page++) {
		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
		info("Probing for which registers that are read/write "
				"for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 old_val, ctrl_val, test_val = 0xff;

			m5602_read_sensor(sd, address, &old_val, 1);
			m5602_write_sensor(sd, address, &test_val, 1);
			m5602_read_sensor(sd, address, &ctrl_val, 1);

			if (ctrl_val == test_val)
				info("register 0x%x is writeable", address);
			else
				info("register 0x%x is read only", address);

			/* Restore original val */
			m5602_write_sensor(sd, address, &old_val, 1);
		}
	}
	info("Read/write register probing complete");
	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
+37 −30
Original line number Diff line number Diff line
@@ -24,17 +24,18 @@
#define S5K83A_FLIP			0x01
#define S5K83A_HFLIP_TUNE		0x03
#define S5K83A_VFLIP_TUNE		0x05
#define S5K83A_WHITENESS			0x0a
#define S5K83A_GAIN				0x18
#define S5K83A_BRIGHTNESS			0x1b
#define S5K83A_BRIGHTNESS		0x0a
#define S5K83A_EXPOSURE			0x18
#define S5K83A_GAIN			0x1b
#define S5K83A_PAGE_MAP			0xec

#define S5K83A_DEFAULT_BRIGHTNESS		0x71
#define S5K83A_DEFAULT_WHITENESS		0x7e
#define S5K83A_DEFAULT_GAIN			0x00
#define S5K83A_MAXIMUM_GAIN			0x3c
#define S5K83A_DEFAULT_GAIN		0x71
#define S5K83A_DEFAULT_BRIGHTNESS	0x7e
#define S5K83A_DEFAULT_EXPOSURE		0x00
#define S5K83A_MAXIMUM_EXPOSURE		0x3c
#define S5K83A_FLIP_MASK		0x10
#define S5K83A_GPIO_LED_MASK		0x10
#define S5K83A_GPIO_ROTATION_MASK 	0x40

/*****************************************************************************/

@@ -47,15 +48,14 @@ int s5k83a_init(struct sd *sd);
int s5k83a_start(struct sd *sd);
int s5k83a_stop(struct sd *sd);
int s5k83a_power_down(struct sd *sd);
void s5k83a_disconnect(struct sd *sd);

int s5k83a_set_led_indication(struct sd *sd, u8 val);

int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -68,10 +68,18 @@ static const struct m5602_sensor s5k83a = {
	.start = s5k83a_start,
	.stop = s5k83a_stop,
	.power_down = s5k83a_power_down,
	.disconnect = s5k83a_disconnect,
	.i2c_slave_id = 0x5a,
	.i2c_regW = 2,
};

struct s5k83a_priv {
	/* We use another thread periodically
	   probing the orientation of the camera */
	struct task_struct *rotation_thread;
	s32 *settings;
};

static const unsigned char preinit_s5k83a[][4] =
{
	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
@@ -125,7 +133,7 @@ static const unsigned char init_s5k83a[][4] =
	{SENSOR, 0x01, 0x50, 0x00},
	{SENSOR, 0x12, 0x20, 0x00},
	{SENSOR, 0x17, 0x40, 0x00},
	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
	{SENSOR, S5K83A_GAIN, 0x0f, 0x00},
	{SENSOR, 0x1c, 0x00, 0x00},
	{SENSOR, 0x02, 0x70, 0x00},
	{SENSOR, 0x03, 0x0b, 0x00},
@@ -232,7 +240,7 @@ static const unsigned char init_s5k83a[][4] =
	{SENSOR, 0x01, 0x50, 0x00},
	{SENSOR, 0x12, 0x20, 0x00},
	{SENSOR, 0x17, 0x40, 0x00},
	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
	{SENSOR, S5K83A_GAIN, 0x0f, 0x00},
	{SENSOR, 0x1c, 0x00, 0x00},
	{SENSOR, 0x02, 0x70, 0x00},
	/* some values like 0x10 give a blue-purple image */
@@ -320,7 +328,7 @@ static const unsigned char init_s5k83a[][4] =
	{SENSOR, 0x01, 0x50, 0x00},
	{SENSOR, 0x12, 0x20, 0x00},
	{SENSOR, 0x17, 0x40, 0x00},
	{SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
	{SENSOR, S5K83A_GAIN, 0x0f, 0x00},
	{SENSOR, 0x1c, 0x00, 0x00},
	{SENSOR, 0x02, 0x70, 0x00},
	{SENSOR, 0x03, 0x0b, 0x00},
@@ -374,24 +382,23 @@ static const unsigned char init_s5k83a[][4] =
	   (this is value after boot, but after tries can be different) */
	{SENSOR, 0x00, 0x06, 0x00},

	/* set default brightness */
	/* set default gain */
	{SENSOR_LONG, 0x14, 0x00, 0x20},
	{SENSOR_LONG, 0x0d, 0x01, 0x00},
	{SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3,
			    S5K83A_DEFAULT_BRIGHTNESS >> 1},
	{SENSOR_LONG, 0x1b, S5K83A_DEFAULT_GAIN >> 3,
		S5K83A_DEFAULT_GAIN >> 1},

	/* set default whiteness */
	{SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00},
	/* set default brightness */
	{SENSOR, S5K83A_BRIGHTNESS, S5K83A_DEFAULT_BRIGHTNESS, 0x00},

	/* set default gain */
	{SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN},
	/* set default exposure */
	{SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_EXPOSURE},

	/* set default flip */
	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
	{SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00},
	{SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00},
	{SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00}

};

#endif