10c0d06caSMauro Carvalho Chehab /*
20c0d06caSMauro Carvalho Chehab  * Driver for the mt9m111 sensor
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  * Copyright (C) 2008 Erik Andrén
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
60c0d06caSMauro Carvalho Chehab  * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
70c0d06caSMauro Carvalho Chehab  *
80c0d06caSMauro Carvalho Chehab  * Portions of code to USB interface and ALi driver software,
90c0d06caSMauro Carvalho Chehab  * Copyright (c) 2006 Willem Duinker
100c0d06caSMauro Carvalho Chehab  * v4l2 interface modeled after the V4L2 driver
110c0d06caSMauro Carvalho Chehab  * for SN9C10x PC Camera Controllers
120c0d06caSMauro Carvalho Chehab  *
130c0d06caSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
140c0d06caSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License as
150c0d06caSMauro Carvalho Chehab  * published by the Free Software Foundation, version 2.
160c0d06caSMauro Carvalho Chehab  *
170c0d06caSMauro Carvalho Chehab  */
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab #include "m5602_mt9m111.h"
220c0d06caSMauro Carvalho Chehab 
230c0d06caSMauro Carvalho Chehab static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
240c0d06caSMauro Carvalho Chehab static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
250c0d06caSMauro Carvalho Chehab static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
260c0d06caSMauro Carvalho Chehab static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
270c0d06caSMauro Carvalho Chehab static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
280c0d06caSMauro Carvalho Chehab static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val);
290c0d06caSMauro Carvalho Chehab static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
300c0d06caSMauro Carvalho Chehab 					 __s32 val);
310c0d06caSMauro Carvalho Chehab static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
320c0d06caSMauro Carvalho Chehab 					  __s32 *val);
330c0d06caSMauro Carvalho Chehab static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);
340c0d06caSMauro Carvalho Chehab static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);
350c0d06caSMauro Carvalho Chehab static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
360c0d06caSMauro Carvalho Chehab static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
370c0d06caSMauro Carvalho Chehab static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
380c0d06caSMauro Carvalho Chehab static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
390c0d06caSMauro Carvalho Chehab 
400c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format mt9m111_modes[] = {
410c0d06caSMauro Carvalho Chehab 	{
420c0d06caSMauro Carvalho Chehab 		640,
430c0d06caSMauro Carvalho Chehab 		480,
440c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SBGGR8,
450c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
460c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480,
470c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
480c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
490c0d06caSMauro Carvalho Chehab 		.priv = 0
500c0d06caSMauro Carvalho Chehab 	}
510c0d06caSMauro Carvalho Chehab };
520c0d06caSMauro Carvalho Chehab 
530c0d06caSMauro Carvalho Chehab static const struct ctrl mt9m111_ctrls[] = {
540c0d06caSMauro Carvalho Chehab #define VFLIP_IDX 0
550c0d06caSMauro Carvalho Chehab 	{
560c0d06caSMauro Carvalho Chehab 		{
570c0d06caSMauro Carvalho Chehab 			.id		= V4L2_CID_VFLIP,
580c0d06caSMauro Carvalho Chehab 			.type           = V4L2_CTRL_TYPE_BOOLEAN,
590c0d06caSMauro Carvalho Chehab 			.name           = "vertical flip",
600c0d06caSMauro Carvalho Chehab 			.minimum        = 0,
610c0d06caSMauro Carvalho Chehab 			.maximum        = 1,
620c0d06caSMauro Carvalho Chehab 			.step           = 1,
630c0d06caSMauro Carvalho Chehab 			.default_value  = 0
640c0d06caSMauro Carvalho Chehab 		},
650c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_vflip,
660c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_vflip
670c0d06caSMauro Carvalho Chehab 	},
680c0d06caSMauro Carvalho Chehab #define HFLIP_IDX 1
690c0d06caSMauro Carvalho Chehab 	{
700c0d06caSMauro Carvalho Chehab 		{
710c0d06caSMauro Carvalho Chehab 			.id             = V4L2_CID_HFLIP,
720c0d06caSMauro Carvalho Chehab 			.type           = V4L2_CTRL_TYPE_BOOLEAN,
730c0d06caSMauro Carvalho Chehab 			.name           = "horizontal flip",
740c0d06caSMauro Carvalho Chehab 			.minimum        = 0,
750c0d06caSMauro Carvalho Chehab 			.maximum        = 1,
760c0d06caSMauro Carvalho Chehab 			.step           = 1,
770c0d06caSMauro Carvalho Chehab 			.default_value  = 0
780c0d06caSMauro Carvalho Chehab 		},
790c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_hflip,
800c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_hflip
810c0d06caSMauro Carvalho Chehab 	},
820c0d06caSMauro Carvalho Chehab #define GAIN_IDX 2
830c0d06caSMauro Carvalho Chehab 	{
840c0d06caSMauro Carvalho Chehab 		{
850c0d06caSMauro Carvalho Chehab 			.id             = V4L2_CID_GAIN,
860c0d06caSMauro Carvalho Chehab 			.type           = V4L2_CTRL_TYPE_INTEGER,
870c0d06caSMauro Carvalho Chehab 			.name           = "gain",
880c0d06caSMauro Carvalho Chehab 			.minimum        = 0,
890c0d06caSMauro Carvalho Chehab 			.maximum        = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
900c0d06caSMauro Carvalho Chehab 			.step           = 1,
910c0d06caSMauro Carvalho Chehab 			.default_value  = MT9M111_DEFAULT_GAIN,
920c0d06caSMauro Carvalho Chehab 			.flags          = V4L2_CTRL_FLAG_SLIDER
930c0d06caSMauro Carvalho Chehab 		},
940c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_gain,
950c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_gain
960c0d06caSMauro Carvalho Chehab 	},
970c0d06caSMauro Carvalho Chehab #define AUTO_WHITE_BALANCE_IDX 3
980c0d06caSMauro Carvalho Chehab 	{
990c0d06caSMauro Carvalho Chehab 		{
1000c0d06caSMauro Carvalho Chehab 			.id             = V4L2_CID_AUTO_WHITE_BALANCE,
1010c0d06caSMauro Carvalho Chehab 			.type           = V4L2_CTRL_TYPE_BOOLEAN,
1020c0d06caSMauro Carvalho Chehab 			.name           = "auto white balance",
1030c0d06caSMauro Carvalho Chehab 			.minimum        = 0,
1040c0d06caSMauro Carvalho Chehab 			.maximum        = 1,
1050c0d06caSMauro Carvalho Chehab 			.step           = 1,
1060c0d06caSMauro Carvalho Chehab 			.default_value  = 0,
1070c0d06caSMauro Carvalho Chehab 		},
1080c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_auto_white_balance,
1090c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_auto_white_balance
1100c0d06caSMauro Carvalho Chehab 	},
1110c0d06caSMauro Carvalho Chehab #define GREEN_BALANCE_IDX 4
1120c0d06caSMauro Carvalho Chehab 	{
1130c0d06caSMauro Carvalho Chehab 		{
1140c0d06caSMauro Carvalho Chehab 			.id		= M5602_V4L2_CID_GREEN_BALANCE,
1150c0d06caSMauro Carvalho Chehab 			.type		= V4L2_CTRL_TYPE_INTEGER,
1160c0d06caSMauro Carvalho Chehab 			.name		= "green balance",
1170c0d06caSMauro Carvalho Chehab 			.minimum	= 0x00,
1180c0d06caSMauro Carvalho Chehab 			.maximum	= 0x7ff,
1190c0d06caSMauro Carvalho Chehab 			.step		= 0x1,
1200c0d06caSMauro Carvalho Chehab 			.default_value	= MT9M111_GREEN_GAIN_DEFAULT,
1210c0d06caSMauro Carvalho Chehab 			.flags		= V4L2_CTRL_FLAG_SLIDER
1220c0d06caSMauro Carvalho Chehab 		},
1230c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_green_balance,
1240c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_green_balance
1250c0d06caSMauro Carvalho Chehab 	},
1260c0d06caSMauro Carvalho Chehab #define BLUE_BALANCE_IDX 5
1270c0d06caSMauro Carvalho Chehab 	{
1280c0d06caSMauro Carvalho Chehab 		{
1290c0d06caSMauro Carvalho Chehab 			.id		= V4L2_CID_BLUE_BALANCE,
1300c0d06caSMauro Carvalho Chehab 			.type		= V4L2_CTRL_TYPE_INTEGER,
1310c0d06caSMauro Carvalho Chehab 			.name		= "blue balance",
1320c0d06caSMauro Carvalho Chehab 			.minimum	= 0x00,
1330c0d06caSMauro Carvalho Chehab 			.maximum	= 0x7ff,
1340c0d06caSMauro Carvalho Chehab 			.step		= 0x1,
1350c0d06caSMauro Carvalho Chehab 			.default_value	= MT9M111_BLUE_GAIN_DEFAULT,
1360c0d06caSMauro Carvalho Chehab 			.flags		= V4L2_CTRL_FLAG_SLIDER
1370c0d06caSMauro Carvalho Chehab 		},
1380c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_blue_balance,
1390c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_blue_balance
1400c0d06caSMauro Carvalho Chehab 	},
1410c0d06caSMauro Carvalho Chehab #define RED_BALANCE_IDX 5
1420c0d06caSMauro Carvalho Chehab 	{
1430c0d06caSMauro Carvalho Chehab 		{
1440c0d06caSMauro Carvalho Chehab 			.id		= V4L2_CID_RED_BALANCE,
1450c0d06caSMauro Carvalho Chehab 			.type		= V4L2_CTRL_TYPE_INTEGER,
1460c0d06caSMauro Carvalho Chehab 			.name		= "red balance",
1470c0d06caSMauro Carvalho Chehab 			.minimum	= 0x00,
1480c0d06caSMauro Carvalho Chehab 			.maximum	= 0x7ff,
1490c0d06caSMauro Carvalho Chehab 			.step		= 0x1,
1500c0d06caSMauro Carvalho Chehab 			.default_value	= MT9M111_RED_GAIN_DEFAULT,
1510c0d06caSMauro Carvalho Chehab 			.flags		= V4L2_CTRL_FLAG_SLIDER
1520c0d06caSMauro Carvalho Chehab 		},
1530c0d06caSMauro Carvalho Chehab 		.set = mt9m111_set_red_balance,
1540c0d06caSMauro Carvalho Chehab 		.get = mt9m111_get_red_balance
1550c0d06caSMauro Carvalho Chehab 	},
1560c0d06caSMauro Carvalho Chehab };
1570c0d06caSMauro Carvalho Chehab 
1580c0d06caSMauro Carvalho Chehab static void mt9m111_dump_registers(struct sd *sd);
1590c0d06caSMauro Carvalho Chehab 
1600c0d06caSMauro Carvalho Chehab int mt9m111_probe(struct sd *sd)
1610c0d06caSMauro Carvalho Chehab {
1620c0d06caSMauro Carvalho Chehab 	u8 data[2] = {0x00, 0x00};
1630c0d06caSMauro Carvalho Chehab 	int i;
1640c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings;
1650c0d06caSMauro Carvalho Chehab 
1660c0d06caSMauro Carvalho Chehab 	if (force_sensor) {
1670c0d06caSMauro Carvalho Chehab 		if (force_sensor == MT9M111_SENSOR) {
1680c0d06caSMauro Carvalho Chehab 			pr_info("Forcing a %s sensor\n", mt9m111.name);
1690c0d06caSMauro Carvalho Chehab 			goto sensor_found;
1700c0d06caSMauro Carvalho Chehab 		}
1710c0d06caSMauro Carvalho Chehab 		/* If we want to force another sensor, don't try to probe this
1720c0d06caSMauro Carvalho Chehab 		 * one */
1730c0d06caSMauro Carvalho Chehab 		return -ENODEV;
1740c0d06caSMauro Carvalho Chehab 	}
1750c0d06caSMauro Carvalho Chehab 
1760c0d06caSMauro Carvalho Chehab 	PDEBUG(D_PROBE, "Probing for a mt9m111 sensor");
1770c0d06caSMauro Carvalho Chehab 
1780c0d06caSMauro Carvalho Chehab 	/* Do the preinit */
1790c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
1800c0d06caSMauro Carvalho Chehab 		if (preinit_mt9m111[i][0] == BRIDGE) {
1810c0d06caSMauro Carvalho Chehab 			m5602_write_bridge(sd,
1820c0d06caSMauro Carvalho Chehab 				preinit_mt9m111[i][1],
1830c0d06caSMauro Carvalho Chehab 				preinit_mt9m111[i][2]);
1840c0d06caSMauro Carvalho Chehab 		} else {
1850c0d06caSMauro Carvalho Chehab 			data[0] = preinit_mt9m111[i][2];
1860c0d06caSMauro Carvalho Chehab 			data[1] = preinit_mt9m111[i][3];
1870c0d06caSMauro Carvalho Chehab 			m5602_write_sensor(sd,
1880c0d06caSMauro Carvalho Chehab 				preinit_mt9m111[i][1], data, 2);
1890c0d06caSMauro Carvalho Chehab 		}
1900c0d06caSMauro Carvalho Chehab 	}
1910c0d06caSMauro Carvalho Chehab 
1920c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
1930c0d06caSMauro Carvalho Chehab 		return -ENODEV;
1940c0d06caSMauro Carvalho Chehab 
1950c0d06caSMauro Carvalho Chehab 	if ((data[0] == 0x14) && (data[1] == 0x3a)) {
1960c0d06caSMauro Carvalho Chehab 		pr_info("Detected a mt9m111 sensor\n");
1970c0d06caSMauro Carvalho Chehab 		goto sensor_found;
1980c0d06caSMauro Carvalho Chehab 	}
1990c0d06caSMauro Carvalho Chehab 
2000c0d06caSMauro Carvalho Chehab 	return -ENODEV;
2010c0d06caSMauro Carvalho Chehab 
2020c0d06caSMauro Carvalho Chehab sensor_found:
2030c0d06caSMauro Carvalho Chehab 	sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32),
2040c0d06caSMauro Carvalho Chehab 				  GFP_KERNEL);
2050c0d06caSMauro Carvalho Chehab 	if (!sensor_settings)
2060c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
2070c0d06caSMauro Carvalho Chehab 
2080c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.cam_mode = mt9m111_modes;
2090c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes);
2100c0d06caSMauro Carvalho Chehab 	sd->desc->ctrls = mt9m111_ctrls;
2110c0d06caSMauro Carvalho Chehab 	sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls);
2120c0d06caSMauro Carvalho Chehab 
2130c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++)
2140c0d06caSMauro Carvalho Chehab 		sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value;
2150c0d06caSMauro Carvalho Chehab 	sd->sensor_priv = sensor_settings;
2160c0d06caSMauro Carvalho Chehab 
2170c0d06caSMauro Carvalho Chehab 	return 0;
2180c0d06caSMauro Carvalho Chehab }
2190c0d06caSMauro Carvalho Chehab 
2200c0d06caSMauro Carvalho Chehab int mt9m111_init(struct sd *sd)
2210c0d06caSMauro Carvalho Chehab {
2220c0d06caSMauro Carvalho Chehab 	int i, err = 0;
2230c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
2240c0d06caSMauro Carvalho Chehab 
2250c0d06caSMauro Carvalho Chehab 	/* Init the sensor */
2260c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
2270c0d06caSMauro Carvalho Chehab 		u8 data[2];
2280c0d06caSMauro Carvalho Chehab 
2290c0d06caSMauro Carvalho Chehab 		if (init_mt9m111[i][0] == BRIDGE) {
2300c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
2310c0d06caSMauro Carvalho Chehab 				init_mt9m111[i][1],
2320c0d06caSMauro Carvalho Chehab 				init_mt9m111[i][2]);
2330c0d06caSMauro Carvalho Chehab 		} else {
2340c0d06caSMauro Carvalho Chehab 			data[0] = init_mt9m111[i][2];
2350c0d06caSMauro Carvalho Chehab 			data[1] = init_mt9m111[i][3];
2360c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
2370c0d06caSMauro Carvalho Chehab 				init_mt9m111[i][1], data, 2);
2380c0d06caSMauro Carvalho Chehab 		}
2390c0d06caSMauro Carvalho Chehab 	}
2400c0d06caSMauro Carvalho Chehab 
2410c0d06caSMauro Carvalho Chehab 	if (dump_sensor)
2420c0d06caSMauro Carvalho Chehab 		mt9m111_dump_registers(sd);
2430c0d06caSMauro Carvalho Chehab 
2440c0d06caSMauro Carvalho Chehab 	err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
2450c0d06caSMauro Carvalho Chehab 	if (err < 0)
2460c0d06caSMauro Carvalho Chehab 		return err;
2470c0d06caSMauro Carvalho Chehab 
2480c0d06caSMauro Carvalho Chehab 	err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
2490c0d06caSMauro Carvalho Chehab 	if (err < 0)
2500c0d06caSMauro Carvalho Chehab 		return err;
2510c0d06caSMauro Carvalho Chehab 
2520c0d06caSMauro Carvalho Chehab 	err = mt9m111_set_green_balance(&sd->gspca_dev,
2530c0d06caSMauro Carvalho Chehab 					 sensor_settings[GREEN_BALANCE_IDX]);
2540c0d06caSMauro Carvalho Chehab 	if (err < 0)
2550c0d06caSMauro Carvalho Chehab 		return err;
2560c0d06caSMauro Carvalho Chehab 
2570c0d06caSMauro Carvalho Chehab 	err = mt9m111_set_blue_balance(&sd->gspca_dev,
2580c0d06caSMauro Carvalho Chehab 					 sensor_settings[BLUE_BALANCE_IDX]);
2590c0d06caSMauro Carvalho Chehab 	if (err < 0)
2600c0d06caSMauro Carvalho Chehab 		return err;
2610c0d06caSMauro Carvalho Chehab 
2620c0d06caSMauro Carvalho Chehab 	err = mt9m111_set_red_balance(&sd->gspca_dev,
2630c0d06caSMauro Carvalho Chehab 					sensor_settings[RED_BALANCE_IDX]);
2640c0d06caSMauro Carvalho Chehab 	if (err < 0)
2650c0d06caSMauro Carvalho Chehab 		return err;
2660c0d06caSMauro Carvalho Chehab 
2670c0d06caSMauro Carvalho Chehab 	return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
2680c0d06caSMauro Carvalho Chehab }
2690c0d06caSMauro Carvalho Chehab 
2700c0d06caSMauro Carvalho Chehab int mt9m111_start(struct sd *sd)
2710c0d06caSMauro Carvalho Chehab {
2720c0d06caSMauro Carvalho Chehab 	int i, err = 0;
2730c0d06caSMauro Carvalho Chehab 	u8 data[2];
2740c0d06caSMauro Carvalho Chehab 	struct cam *cam = &sd->gspca_dev.cam;
2750c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
2760c0d06caSMauro Carvalho Chehab 
2770c0d06caSMauro Carvalho Chehab 	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1;
2780c0d06caSMauro Carvalho Chehab 	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
2790c0d06caSMauro Carvalho Chehab 
2800c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) {
2810c0d06caSMauro Carvalho Chehab 		if (start_mt9m111[i][0] == BRIDGE) {
2820c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
2830c0d06caSMauro Carvalho Chehab 				start_mt9m111[i][1],
2840c0d06caSMauro Carvalho Chehab 				start_mt9m111[i][2]);
2850c0d06caSMauro Carvalho Chehab 		} else {
2860c0d06caSMauro Carvalho Chehab 			data[0] = start_mt9m111[i][2];
2870c0d06caSMauro Carvalho Chehab 			data[1] = start_mt9m111[i][3];
2880c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
2890c0d06caSMauro Carvalho Chehab 				start_mt9m111[i][1], data, 2);
2900c0d06caSMauro Carvalho Chehab 		}
2910c0d06caSMauro Carvalho Chehab 	}
2920c0d06caSMauro Carvalho Chehab 	if (err < 0)
2930c0d06caSMauro Carvalho Chehab 		return err;
2940c0d06caSMauro Carvalho Chehab 
2950c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
2960c0d06caSMauro Carvalho Chehab 	if (err < 0)
2970c0d06caSMauro Carvalho Chehab 		return err;
2980c0d06caSMauro Carvalho Chehab 
2990c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
3000c0d06caSMauro Carvalho Chehab 	if (err < 0)
3010c0d06caSMauro Carvalho Chehab 		return err;
3020c0d06caSMauro Carvalho Chehab 
3030c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 2 && !err; i++)
3040c0d06caSMauro Carvalho Chehab 		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
3050c0d06caSMauro Carvalho Chehab 	if (err < 0)
3060c0d06caSMauro Carvalho Chehab 		return err;
3070c0d06caSMauro Carvalho Chehab 
3080c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
3090c0d06caSMauro Carvalho Chehab 	if (err < 0)
3100c0d06caSMauro Carvalho Chehab 		return err;
3110c0d06caSMauro Carvalho Chehab 
3120c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
3130c0d06caSMauro Carvalho Chehab 	if (err < 0)
3140c0d06caSMauro Carvalho Chehab 		return err;
3150c0d06caSMauro Carvalho Chehab 
3160c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 2 && !err; i++)
3170c0d06caSMauro Carvalho Chehab 		err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
3180c0d06caSMauro Carvalho Chehab 	if (err < 0)
3190c0d06caSMauro Carvalho Chehab 		return err;
3200c0d06caSMauro Carvalho Chehab 
3210c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
3220c0d06caSMauro Carvalho Chehab 				 (width >> 8) & 0xff);
3230c0d06caSMauro Carvalho Chehab 	if (err < 0)
3240c0d06caSMauro Carvalho Chehab 		return err;
3250c0d06caSMauro Carvalho Chehab 
3260c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff);
3270c0d06caSMauro Carvalho Chehab 	if (err < 0)
3280c0d06caSMauro Carvalho Chehab 		return err;
3290c0d06caSMauro Carvalho Chehab 
3300c0d06caSMauro Carvalho Chehab 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
3310c0d06caSMauro Carvalho Chehab 	if (err < 0)
3320c0d06caSMauro Carvalho Chehab 		return err;
3330c0d06caSMauro Carvalho Chehab 
3340c0d06caSMauro Carvalho Chehab 	switch (width) {
3350c0d06caSMauro Carvalho Chehab 	case 640:
3360c0d06caSMauro Carvalho Chehab 		PDEBUG(D_V4L2, "Configuring camera for VGA mode");
3370c0d06caSMauro Carvalho Chehab 		data[0] = MT9M111_RMB_OVER_SIZED;
3380c0d06caSMauro Carvalho Chehab 		data[1] = MT9M111_RMB_ROW_SKIP_2X |
3390c0d06caSMauro Carvalho Chehab 			  MT9M111_RMB_COLUMN_SKIP_2X |
3400c0d06caSMauro Carvalho Chehab 			  (sensor_settings[VFLIP_IDX] << 0) |
3410c0d06caSMauro Carvalho Chehab 			  (sensor_settings[HFLIP_IDX] << 1);
3420c0d06caSMauro Carvalho Chehab 
3430c0d06caSMauro Carvalho Chehab 		err = m5602_write_sensor(sd,
3440c0d06caSMauro Carvalho Chehab 					 MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
3450c0d06caSMauro Carvalho Chehab 		break;
3460c0d06caSMauro Carvalho Chehab 
3470c0d06caSMauro Carvalho Chehab 	case 320:
3480c0d06caSMauro Carvalho Chehab 		PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
3490c0d06caSMauro Carvalho Chehab 		data[0] = MT9M111_RMB_OVER_SIZED;
3500c0d06caSMauro Carvalho Chehab 		data[1] = MT9M111_RMB_ROW_SKIP_4X |
3510c0d06caSMauro Carvalho Chehab 				MT9M111_RMB_COLUMN_SKIP_4X |
3520c0d06caSMauro Carvalho Chehab 				(sensor_settings[VFLIP_IDX] << 0) |
3530c0d06caSMauro Carvalho Chehab 				(sensor_settings[HFLIP_IDX] << 1);
3540c0d06caSMauro Carvalho Chehab 		err = m5602_write_sensor(sd,
3550c0d06caSMauro Carvalho Chehab 					 MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
3560c0d06caSMauro Carvalho Chehab 		break;
3570c0d06caSMauro Carvalho Chehab 	}
3580c0d06caSMauro Carvalho Chehab 	return err;
3590c0d06caSMauro Carvalho Chehab }
3600c0d06caSMauro Carvalho Chehab 
3610c0d06caSMauro Carvalho Chehab void mt9m111_disconnect(struct sd *sd)
3620c0d06caSMauro Carvalho Chehab {
3630c0d06caSMauro Carvalho Chehab 	sd->sensor = NULL;
3640c0d06caSMauro Carvalho Chehab 	kfree(sd->sensor_priv);
3650c0d06caSMauro Carvalho Chehab }
3660c0d06caSMauro Carvalho Chehab 
3670c0d06caSMauro Carvalho Chehab static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
3680c0d06caSMauro Carvalho Chehab {
3690c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3700c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
3710c0d06caSMauro Carvalho Chehab 
3720c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[VFLIP_IDX];
3730c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read vertical flip %d", *val);
3740c0d06caSMauro Carvalho Chehab 
3750c0d06caSMauro Carvalho Chehab 	return 0;
3760c0d06caSMauro Carvalho Chehab }
3770c0d06caSMauro Carvalho Chehab 
3780c0d06caSMauro Carvalho Chehab static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
3790c0d06caSMauro Carvalho Chehab {
3800c0d06caSMauro Carvalho Chehab 	int err;
3810c0d06caSMauro Carvalho Chehab 	u8 data[2] = {0x00, 0x00};
3820c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3830c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
3840c0d06caSMauro Carvalho Chehab 
3850c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set vertical flip to %d", val);
3860c0d06caSMauro Carvalho Chehab 
3870c0d06caSMauro Carvalho Chehab 	sensor_settings[VFLIP_IDX] = val;
3880c0d06caSMauro Carvalho Chehab 
3890c0d06caSMauro Carvalho Chehab 	/* The mt9m111 is flipped by default */
3900c0d06caSMauro Carvalho Chehab 	val = !val;
3910c0d06caSMauro Carvalho Chehab 
3920c0d06caSMauro Carvalho Chehab 	/* Set the correct page map */
3930c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
3940c0d06caSMauro Carvalho Chehab 	if (err < 0)
3950c0d06caSMauro Carvalho Chehab 		return err;
3960c0d06caSMauro Carvalho Chehab 
3970c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
3980c0d06caSMauro Carvalho Chehab 	if (err < 0)
3990c0d06caSMauro Carvalho Chehab 		return err;
4000c0d06caSMauro Carvalho Chehab 
4010c0d06caSMauro Carvalho Chehab 	data[1] = (data[1] & 0xfe) | val;
4020c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
4030c0d06caSMauro Carvalho Chehab 				   data, 2);
4040c0d06caSMauro Carvalho Chehab 	return err;
4050c0d06caSMauro Carvalho Chehab }
4060c0d06caSMauro Carvalho Chehab 
4070c0d06caSMauro Carvalho Chehab static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
4080c0d06caSMauro Carvalho Chehab {
4090c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4100c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4110c0d06caSMauro Carvalho Chehab 
4120c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[HFLIP_IDX];
4130c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
4140c0d06caSMauro Carvalho Chehab 
4150c0d06caSMauro Carvalho Chehab 	return 0;
4160c0d06caSMauro Carvalho Chehab }
4170c0d06caSMauro Carvalho Chehab 
4180c0d06caSMauro Carvalho Chehab static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
4190c0d06caSMauro Carvalho Chehab {
4200c0d06caSMauro Carvalho Chehab 	int err;
4210c0d06caSMauro Carvalho Chehab 	u8 data[2] = {0x00, 0x00};
4220c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4230c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4240c0d06caSMauro Carvalho Chehab 
4250c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
4260c0d06caSMauro Carvalho Chehab 
4270c0d06caSMauro Carvalho Chehab 	sensor_settings[HFLIP_IDX] = val;
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab 	/* The mt9m111 is flipped by default */
4300c0d06caSMauro Carvalho Chehab 	val = !val;
4310c0d06caSMauro Carvalho Chehab 
4320c0d06caSMauro Carvalho Chehab 	/* Set the correct page map */
4330c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
4340c0d06caSMauro Carvalho Chehab 	if (err < 0)
4350c0d06caSMauro Carvalho Chehab 		return err;
4360c0d06caSMauro Carvalho Chehab 
4370c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
4380c0d06caSMauro Carvalho Chehab 	if (err < 0)
4390c0d06caSMauro Carvalho Chehab 		return err;
4400c0d06caSMauro Carvalho Chehab 
4410c0d06caSMauro Carvalho Chehab 	data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02);
4420c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
4430c0d06caSMauro Carvalho Chehab 					data, 2);
4440c0d06caSMauro Carvalho Chehab 	return err;
4450c0d06caSMauro Carvalho Chehab }
4460c0d06caSMauro Carvalho Chehab 
4470c0d06caSMauro Carvalho Chehab static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
4480c0d06caSMauro Carvalho Chehab {
4490c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4500c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4510c0d06caSMauro Carvalho Chehab 
4520c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[GAIN_IDX];
4530c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read gain %d", *val);
4540c0d06caSMauro Carvalho Chehab 
4550c0d06caSMauro Carvalho Chehab 	return 0;
4560c0d06caSMauro Carvalho Chehab }
4570c0d06caSMauro Carvalho Chehab 
4580c0d06caSMauro Carvalho Chehab static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
4590c0d06caSMauro Carvalho Chehab 					  __s32 val)
4600c0d06caSMauro Carvalho Chehab {
4610c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4620c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4630c0d06caSMauro Carvalho Chehab 	int err;
4640c0d06caSMauro Carvalho Chehab 	u8 data[2];
4650c0d06caSMauro Carvalho Chehab 
4660c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
4670c0d06caSMauro Carvalho Chehab 	if (err < 0)
4680c0d06caSMauro Carvalho Chehab 		return err;
4690c0d06caSMauro Carvalho Chehab 
4700c0d06caSMauro Carvalho Chehab 	sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01;
4710c0d06caSMauro Carvalho Chehab 	data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1));
4720c0d06caSMauro Carvalho Chehab 
4730c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
4740c0d06caSMauro Carvalho Chehab 
4750c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set auto white balance %d", val);
4760c0d06caSMauro Carvalho Chehab 	return err;
4770c0d06caSMauro Carvalho Chehab }
4780c0d06caSMauro Carvalho Chehab 
4790c0d06caSMauro Carvalho Chehab static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
4800c0d06caSMauro Carvalho Chehab 					  __s32 *val) {
4810c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4820c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4830c0d06caSMauro Carvalho Chehab 
4840c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
4850c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read auto white balance %d", *val);
4860c0d06caSMauro Carvalho Chehab 	return 0;
4870c0d06caSMauro Carvalho Chehab }
4880c0d06caSMauro Carvalho Chehab 
4890c0d06caSMauro Carvalho Chehab static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab 	int err, tmp;
4920c0d06caSMauro Carvalho Chehab 	u8 data[2] = {0x00, 0x00};
4930c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4940c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
4950c0d06caSMauro Carvalho Chehab 
4960c0d06caSMauro Carvalho Chehab 	sensor_settings[GAIN_IDX] = val;
4970c0d06caSMauro Carvalho Chehab 
4980c0d06caSMauro Carvalho Chehab 	/* Set the correct page map */
4990c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
5000c0d06caSMauro Carvalho Chehab 	if (err < 0)
5010c0d06caSMauro Carvalho Chehab 		return err;
5020c0d06caSMauro Carvalho Chehab 
5030c0d06caSMauro Carvalho Chehab 	if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
5040c0d06caSMauro Carvalho Chehab 		return -EINVAL;
5050c0d06caSMauro Carvalho Chehab 
5060c0d06caSMauro Carvalho Chehab 	if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
5070c0d06caSMauro Carvalho Chehab 	    (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
5080c0d06caSMauro Carvalho Chehab 		tmp = (1 << 10) | (val << 9) |
5090c0d06caSMauro Carvalho Chehab 				(val << 8) | (val / 8);
5100c0d06caSMauro Carvalho Chehab 	else if ((val >= INITIAL_MAX_GAIN * 2) &&
5110c0d06caSMauro Carvalho Chehab 		 (val <  INITIAL_MAX_GAIN * 2 * 2))
5120c0d06caSMauro Carvalho Chehab 		tmp = (1 << 9) | (1 << 8) | (val / 4);
5130c0d06caSMauro Carvalho Chehab 	else if ((val >= INITIAL_MAX_GAIN) &&
5140c0d06caSMauro Carvalho Chehab 		 (val < INITIAL_MAX_GAIN * 2))
5150c0d06caSMauro Carvalho Chehab 		tmp = (1 << 8) | (val / 2);
5160c0d06caSMauro Carvalho Chehab 	else
5170c0d06caSMauro Carvalho Chehab 		tmp = val;
5180c0d06caSMauro Carvalho Chehab 
5190c0d06caSMauro Carvalho Chehab 	data[1] = (tmp & 0xff);
5200c0d06caSMauro Carvalho Chehab 	data[0] = (tmp & 0xff00) >> 8;
5210c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
5220c0d06caSMauro Carvalho Chehab 	       data[1], data[0]);
5230c0d06caSMauro Carvalho Chehab 
5240c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
5250c0d06caSMauro Carvalho Chehab 				   data, 2);
5260c0d06caSMauro Carvalho Chehab 
5270c0d06caSMauro Carvalho Chehab 	return err;
5280c0d06caSMauro Carvalho Chehab }
5290c0d06caSMauro Carvalho Chehab 
5300c0d06caSMauro Carvalho Chehab static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
5310c0d06caSMauro Carvalho Chehab {
5320c0d06caSMauro Carvalho Chehab 	int err;
5330c0d06caSMauro Carvalho Chehab 	u8 data[2];
5340c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5350c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
5360c0d06caSMauro Carvalho Chehab 
5370c0d06caSMauro Carvalho Chehab 	sensor_settings[GREEN_BALANCE_IDX] = val;
5380c0d06caSMauro Carvalho Chehab 	data[1] = (val & 0xff);
5390c0d06caSMauro Carvalho Chehab 	data[0] = (val & 0xff00) >> 8;
5400c0d06caSMauro Carvalho Chehab 
5410c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set green balance %d", val);
5420c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN,
5430c0d06caSMauro Carvalho Chehab 				 data, 2);
5440c0d06caSMauro Carvalho Chehab 	if (err < 0)
5450c0d06caSMauro Carvalho Chehab 		return err;
5460c0d06caSMauro Carvalho Chehab 
5470c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN,
5480c0d06caSMauro Carvalho Chehab 				  data, 2);
5490c0d06caSMauro Carvalho Chehab }
5500c0d06caSMauro Carvalho Chehab 
5510c0d06caSMauro Carvalho Chehab static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)
5520c0d06caSMauro Carvalho Chehab {
5530c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5540c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
5550c0d06caSMauro Carvalho Chehab 
5560c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[GREEN_BALANCE_IDX];
5570c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read green balance %d", *val);
5580c0d06caSMauro Carvalho Chehab 	return 0;
5590c0d06caSMauro Carvalho Chehab }
5600c0d06caSMauro Carvalho Chehab 
5610c0d06caSMauro Carvalho Chehab static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
5620c0d06caSMauro Carvalho Chehab {
5630c0d06caSMauro Carvalho Chehab 	u8 data[2];
5640c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5650c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
5660c0d06caSMauro Carvalho Chehab 
5670c0d06caSMauro Carvalho Chehab 	sensor_settings[BLUE_BALANCE_IDX] = val;
5680c0d06caSMauro Carvalho Chehab 	data[1] = (val & 0xff);
5690c0d06caSMauro Carvalho Chehab 	data[0] = (val & 0xff00) >> 8;
5700c0d06caSMauro Carvalho Chehab 
5710c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set blue balance %d", val);
5720c0d06caSMauro Carvalho Chehab 
5730c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN,
5740c0d06caSMauro Carvalho Chehab 				  data, 2);
5750c0d06caSMauro Carvalho Chehab }
5760c0d06caSMauro Carvalho Chehab 
5770c0d06caSMauro Carvalho Chehab static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
5780c0d06caSMauro Carvalho Chehab {
5790c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5800c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
5810c0d06caSMauro Carvalho Chehab 
5820c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[BLUE_BALANCE_IDX];
5830c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read blue balance %d", *val);
5840c0d06caSMauro Carvalho Chehab 	return 0;
5850c0d06caSMauro Carvalho Chehab }
5860c0d06caSMauro Carvalho Chehab 
5870c0d06caSMauro Carvalho Chehab static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
5880c0d06caSMauro Carvalho Chehab {
5890c0d06caSMauro Carvalho Chehab 	u8 data[2];
5900c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
5910c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
5920c0d06caSMauro Carvalho Chehab 
5930c0d06caSMauro Carvalho Chehab 	sensor_settings[RED_BALANCE_IDX] = val;
5940c0d06caSMauro Carvalho Chehab 	data[1] = (val & 0xff);
5950c0d06caSMauro Carvalho Chehab 	data[0] = (val & 0xff00) >> 8;
5960c0d06caSMauro Carvalho Chehab 
5970c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Set red balance %d", val);
5980c0d06caSMauro Carvalho Chehab 
5990c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN,
6000c0d06caSMauro Carvalho Chehab 				  data, 2);
6010c0d06caSMauro Carvalho Chehab }
6020c0d06caSMauro Carvalho Chehab 
6030c0d06caSMauro Carvalho Chehab static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
6040c0d06caSMauro Carvalho Chehab {
6050c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
6060c0d06caSMauro Carvalho Chehab 	s32 *sensor_settings = sd->sensor_priv;
6070c0d06caSMauro Carvalho Chehab 
6080c0d06caSMauro Carvalho Chehab 	*val = sensor_settings[RED_BALANCE_IDX];
6090c0d06caSMauro Carvalho Chehab 	PDEBUG(D_V4L2, "Read red balance %d", *val);
6100c0d06caSMauro Carvalho Chehab 	return 0;
6110c0d06caSMauro Carvalho Chehab }
6120c0d06caSMauro Carvalho Chehab 
6130c0d06caSMauro Carvalho Chehab static void mt9m111_dump_registers(struct sd *sd)
6140c0d06caSMauro Carvalho Chehab {
6150c0d06caSMauro Carvalho Chehab 	u8 address, value[2] = {0x00, 0x00};
6160c0d06caSMauro Carvalho Chehab 
6170c0d06caSMauro Carvalho Chehab 	pr_info("Dumping the mt9m111 register state\n");
6180c0d06caSMauro Carvalho Chehab 
6190c0d06caSMauro Carvalho Chehab 	pr_info("Dumping the mt9m111 sensor core registers\n");
6200c0d06caSMauro Carvalho Chehab 	value[1] = MT9M111_SENSOR_CORE;
6210c0d06caSMauro Carvalho Chehab 	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
6220c0d06caSMauro Carvalho Chehab 	for (address = 0; address < 0xff; address++) {
6230c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, value, 2);
6240c0d06caSMauro Carvalho Chehab 		pr_info("register 0x%x contains 0x%x%x\n",
6250c0d06caSMauro Carvalho Chehab 			address, value[0], value[1]);
6260c0d06caSMauro Carvalho Chehab 	}
6270c0d06caSMauro Carvalho Chehab 
6280c0d06caSMauro Carvalho Chehab 	pr_info("Dumping the mt9m111 color pipeline registers\n");
6290c0d06caSMauro Carvalho Chehab 	value[1] = MT9M111_COLORPIPE;
6300c0d06caSMauro Carvalho Chehab 	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
6310c0d06caSMauro Carvalho Chehab 	for (address = 0; address < 0xff; address++) {
6320c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, value, 2);
6330c0d06caSMauro Carvalho Chehab 		pr_info("register 0x%x contains 0x%x%x\n",
6340c0d06caSMauro Carvalho Chehab 			address, value[0], value[1]);
6350c0d06caSMauro Carvalho Chehab 	}
6360c0d06caSMauro Carvalho Chehab 
6370c0d06caSMauro Carvalho Chehab 	pr_info("Dumping the mt9m111 camera control registers\n");
6380c0d06caSMauro Carvalho Chehab 	value[1] = MT9M111_CAMERA_CONTROL;
6390c0d06caSMauro Carvalho Chehab 	m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
6400c0d06caSMauro Carvalho Chehab 	for (address = 0; address < 0xff; address++) {
6410c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, value, 2);
6420c0d06caSMauro Carvalho Chehab 		pr_info("register 0x%x contains 0x%x%x\n",
6430c0d06caSMauro Carvalho Chehab 			address, value[0], value[1]);
6440c0d06caSMauro Carvalho Chehab 	}
6450c0d06caSMauro Carvalho Chehab 
6460c0d06caSMauro Carvalho Chehab 	pr_info("mt9m111 register state dump complete\n");
6470c0d06caSMauro Carvalho Chehab }
648