m5602_ov7660.c revision 36e64d5cec0a210d87e8e8c02566a1cbe24c00f3
14763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén/*
24763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Driver for the ov7660 sensor
34763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén *
44763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Copyright (C) 2009 Erik Andrén
54763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
64763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
74763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén *
84763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Portions of code to USB interface and ALi driver software,
94763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * Copyright (c) 2006 Willem Duinker
104763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * v4l2 interface modeled after the V4L2 driver
114763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * for SN9C10x PC Camera Controllers
124763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén *
134763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * This program is free software; you can redistribute it and/or
144763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * modify it under the terms of the GNU General Public License as
154763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén * published by the Free Software Foundation, version 2.
164763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén *
174763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén */
184763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
194763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén#include "m5602_ov7660.h"
204763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
21c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénstatic int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
22c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénstatic int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val);
23cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrénstatic int ov7660_get_blue_gain(struct gspca_dev *gspca_dev, __s32 *val);
24cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrénstatic int ov7660_set_blue_gain(struct gspca_dev *gspca_dev, __s32 val);
2568fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrénstatic int ov7660_get_red_gain(struct gspca_dev *gspca_dev, __s32 *val);
2668fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrénstatic int ov7660_set_red_gain(struct gspca_dev *gspca_dev, __s32 val);
27f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrénstatic int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
28f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén					 __s32 *val);
29f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrénstatic int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
30f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén					 __s32 val);
31f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
32c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
33c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénconst static struct ctrl ov7660_ctrls[] = {
34c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén#define GAIN_IDX 1
35c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	{
36c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		{
37c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.id		= V4L2_CID_GAIN,
38c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.type		= V4L2_CTRL_TYPE_INTEGER,
39c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.name		= "gain",
40c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.minimum	= 0x00,
41c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.maximum	= 0xff,
42c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.step		= 0x1,
4372b79747a88f1ffcb6689cedacc01235bb545cd6Erik Andrén			.default_value	= OV7660_DEFAULT_GAIN,
44c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén			.flags		= V4L2_CTRL_FLAG_SLIDER
45c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		},
46c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		.set = ov7660_set_gain,
47c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		.get = ov7660_get_gain
48c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	},
49cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén#define BLUE_BALANCE_IDX 2
50cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	{
51cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén		{
52cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.id		= V4L2_CID_BLUE_BALANCE,
53cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.type		= V4L2_CTRL_TYPE_INTEGER,
54cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.name		= "blue balance",
55cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.minimum	= 0x00,
56cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.maximum	= 0x7f,
57cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.step		= 0x1,
58cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.default_value	= OV7660_DEFAULT_BLUE_GAIN,
59cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén			.flags		= V4L2_CTRL_FLAG_SLIDER
60cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén		},
61cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén		.set = ov7660_set_blue_gain,
62cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén		.get = ov7660_get_blue_gain
63cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	},
6468fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén#define RED_BALANCE_IDX 3
6568fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	{
6668fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén		{
6768fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.id		= V4L2_CID_RED_BALANCE,
6868fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.type		= V4L2_CTRL_TYPE_INTEGER,
6968fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.name		= "red balance",
7068fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.minimum	= 0x00,
7168fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.maximum	= 0x7f,
7268fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.step		= 0x1,
7368fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.default_value	= OV7660_DEFAULT_RED_GAIN,
7468fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén			.flags		= V4L2_CTRL_FLAG_SLIDER
7568fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén		},
7668fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén		.set = ov7660_set_red_gain,
7768fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén		.get = ov7660_get_red_gain
7868fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	},
79f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén#define AUTO_WHITE_BALANCE_IDX 4
80f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	{
81f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		{
82f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.id 		= V4L2_CID_AUTO_WHITE_BALANCE,
83f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
84f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.name 		= "auto white balance",
85f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.minimum 	= 0,
86f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.maximum 	= 1,
87f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.step 		= 1,
88f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén			.default_value 	= 1
89f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		},
90f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		.set = ov7660_set_auto_white_balance,
91f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		.get = ov7660_get_auto_white_balance
92f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	},
93f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
94c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén};
954763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
964763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénstatic struct v4l2_pix_format ov7660_modes[] = {
974763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	{
984763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		640,
994763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		480,
1004763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		V4L2_PIX_FMT_SBGGR8,
1014763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		V4L2_FIELD_NONE,
1024763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		.sizeimage =
1034763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén			640 * 480,
1044763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		.bytesperline = 640,
1054763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		.colorspace = V4L2_COLORSPACE_SRGB,
1064763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		.priv = 0
1074763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	}
1084763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén};
1094763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
1104763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénstatic void ov7660_dump_registers(struct sd *sd);
1114763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
1124763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénint ov7660_probe(struct sd *sd)
1134763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén{
114ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	int err = 0, i;
115ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	u8 prod_id = 0, ver_id = 0;
116ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
117ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	s32 *sensor_settings;
118ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
119ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	if (force_sensor) {
120ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		if (force_sensor == OV7660_SENSOR) {
121ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén			info("Forcing an %s sensor", ov7660.name);
122ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén			goto sensor_found;
123ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		}
124ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		/* If we want to force another sensor,
125ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		don't try to probe this one */
126ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		return -ENODEV;
127ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	}
128ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
129ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	/* Do the preinit */
130ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) {
131ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		u8 data[2];
132ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
133ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		if (preinit_ov7660[i][0] == BRIDGE) {
134ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén			err = m5602_write_bridge(sd,
135ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén				preinit_ov7660[i][1],
136ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén				preinit_ov7660[i][2]);
137ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		} else {
138ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén			data[0] = preinit_ov7660[i][2];
139ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén			err = m5602_write_sensor(sd,
140ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén				preinit_ov7660[i][1], data, 1);
141ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		}
142ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	}
143ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	if (err < 0)
144ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		return err;
145ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
146ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1))
147ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		return -ENODEV;
148ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
149ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
150ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		return -ENODEV;
151ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
152ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	info("Sensor reported 0x%x%x", prod_id, ver_id);
153ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
154ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	if ((prod_id == 0x76) && (ver_id == 0x60)) {
155ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		info("Detected a ov7660 sensor");
156ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		goto sensor_found;
157ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	}
1584763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	return -ENODEV;
159ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
160ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrénsensor_found:
161c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	sensor_settings = kmalloc(
162c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL);
163c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	if (!sensor_settings)
164c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		return -ENOMEM;
165c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
166ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	sd->gspca_dev.cam.cam_mode = ov7660_modes;
167ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
168ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	sd->desc->ctrls = ov7660_ctrls;
169ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls);
170ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
171ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++)
172ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén		sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value;
173ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	sd->sensor_priv = sensor_settings;
174ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén
175ea8f74b168aa4d725c6d1ab98ef4eb4f6f3f3358Erik Andrén	return 0;
1764763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén}
1774763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
1784763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénint ov7660_init(struct sd *sd)
1794763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén{
1800364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	int i, err = 0;
181c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	s32 *sensor_settings = sd->sensor_priv;
1820364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén
1830364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	/* Init the sensor */
1840364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) {
1850364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén		u8 data[2];
1860364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén
1870364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén		if (init_ov7660[i][0] == BRIDGE) {
1880364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén			err = m5602_write_bridge(sd,
1890364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén				init_ov7660[i][1],
1900364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén				init_ov7660[i][2]);
1910364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén		} else {
1920364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén			data[0] = init_ov7660[i][2];
1930364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén			err = m5602_write_sensor(sd,
1940364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén					init_ov7660[i][1], data, 1);
1950364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén		}
1960364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	}
1970364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén
1980364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	if (dump_sensor)
1990364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén		ov7660_dump_registers(sd);
2000364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén
201c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
202c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	if (err < 0)
203c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén		return err;
204c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
205f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	err = ov7660_set_auto_white_balance(&sd->gspca_dev,
206f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		sensor_settings[AUTO_WHITE_BALANCE_IDX]);
20736e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén	if (err < 0)
20836e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén		return err;
20936e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén
21036e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén	err = ov7660_set_blue_gain(&sd->gspca_dev,
21136e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén		sensor_settings[BLUE_BALANCE_IDX]);
21236e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén	if (err < 0)
21336e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén		return err;
21436e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén
21536e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén	err = ov7660_set_red_gain(&sd->gspca_dev,
21636e64d5cec0a210d87e8e8c02566a1cbe24c00f3Erik Andrén		sensor_settings[RED_BALANCE_IDX]);
217f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
2180364c4ca345175daa36c3c672fdecafd469e05a8Erik Andrén	return err;
2194763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén}
2204763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
2214763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénint ov7660_start(struct sd *sd)
2224763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén{
2234763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	return 0;
2244763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén}
2254763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
2264763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénint ov7660_stop(struct sd *sd)
2274763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén{
2284763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	return 0;
2294763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén}
2304763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
231c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénvoid ov7660_disconnect(struct sd *sd)
232c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén{
233c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	ov7660_stop(sd);
234c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
235c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	sd->sensor = NULL;
236c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	kfree(sd->sensor_priv);
237c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén}
238c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
239c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénstatic int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
240c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén{
241c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	struct sd *sd = (struct sd *) gspca_dev;
242c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	s32 *sensor_settings = sd->sensor_priv;
243c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
244c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	*val = sensor_settings[GAIN_IDX];
245c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	PDEBUG(D_V4L2, "Read gain %d", *val);
246c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	return 0;
247c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén}
248c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
249c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrénstatic int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
250c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén{
251c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	int err;
252c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	u8 i2c_data;
253c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	struct sd *sd = (struct sd *) gspca_dev;
254c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	s32 *sensor_settings = sd->sensor_priv;
255c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
256c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	PDEBUG(D_V4L2, "Setting gain to %d", val);
257c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
258c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	sensor_settings[GAIN_IDX] = val;
259c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén
260c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
261c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén	return err;
262c9304e43fb71ad790c5fc995de55e7c95abe5b4aErik Andrén}
2634763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
264cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrénstatic int ov7660_get_blue_gain(struct gspca_dev *gspca_dev, __s32 *val)
265cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén{
266cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	struct sd *sd = (struct sd *) gspca_dev;
267cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	s32 *sensor_settings = sd->sensor_priv;
268cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
269cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	*val = sensor_settings[BLUE_BALANCE_IDX];
270cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	PDEBUG(D_V4L2, "Read blue balance %d", *val);
271cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	return 0;
272cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén}
273cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
274cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrénstatic int ov7660_set_blue_gain(struct gspca_dev *gspca_dev, __s32 val)
275cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén{
276cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	int err;
277cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	u8 i2c_data;
278cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	struct sd *sd = (struct sd *) gspca_dev;
279cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	s32 *sensor_settings = sd->sensor_priv;
280cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
281cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	PDEBUG(D_V4L2, "Setting blue balance to %d", val);
282cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
283cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	sensor_settings[BLUE_BALANCE_IDX] = val;
284cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
285cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	err = m5602_write_sensor(sd, OV7660_BLUE_GAIN, &i2c_data, 1);
286cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén	return err;
287cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén}
288cbd1f7fb7059c629cb9e5131a755febc906496a6Erik Andrén
28968fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrénstatic int ov7660_get_red_gain(struct gspca_dev *gspca_dev, __s32 *val)
29068fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén{
29168fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	struct sd *sd = (struct sd *) gspca_dev;
29268fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	s32 *sensor_settings = sd->sensor_priv;
29368fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
29468fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	*val = sensor_settings[RED_BALANCE_IDX];
29568fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	PDEBUG(D_V4L2, "Read red balance %d", *val);
29668fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	return 0;
29768fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén}
29868fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
29968fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrénstatic int ov7660_set_red_gain(struct gspca_dev *gspca_dev, __s32 val)
30068fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén{
30168fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	int err;
30268fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	u8 i2c_data;
30368fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	struct sd *sd = (struct sd *) gspca_dev;
30468fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	s32 *sensor_settings = sd->sensor_priv;
30568fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
30668fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	PDEBUG(D_V4L2, "Setting red balance to %d", val);
30768fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
30868fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	sensor_settings[RED_BALANCE_IDX] = val;
30968fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
31068fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	err = m5602_write_sensor(sd, OV7660_RED_GAIN, &i2c_data, 1);
31168fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén	return err;
31268fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén}
31368fdb7a5b52cb060f7824f9d6e98e105af36a474Erik Andrén
314f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrénstatic int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
315f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén					 __s32 *val)
316f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén{
317f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	struct sd *sd = (struct sd *) gspca_dev;
318f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	s32 *sensor_settings = sd->sensor_priv;
319f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
320f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
321f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	return 0;
322f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén}
323f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
324f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrénstatic int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
325f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén					 __s32 val)
326f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén{
327f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	int err;
328f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	u8 i2c_data;
329f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	struct sd *sd = (struct sd *) gspca_dev;
330f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	s32 *sensor_settings = sd->sensor_priv;
331f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
332f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	PDEBUG(D_V4L2, "Set auto white balance to %d", val);
333f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
334f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
335f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
336f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	if (err < 0)
337f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén		return err;
338f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
339f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
340f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
341f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
342f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén	return err;
343f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén}
344f1f59fe60e38c4c56b8acba9690cd08d86d2ac0eErik Andrén
3454763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrénstatic void ov7660_dump_registers(struct sd *sd)
3464763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén{
3474763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	int address;
3484763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	info("Dumping the ov7660 register state");
3494763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	for (address = 0; address < 0xa9; address++) {
3504763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		u8 value;
3514763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		m5602_read_sensor(sd, address, &value, 1);
3524763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		info("register 0x%x contains 0x%x",
3534763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		     address, value);
3544763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	}
3554763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
3564763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	info("ov7660 register state dump complete");
3574763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
3584763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	info("Probing for which registers that are read/write");
3594763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	for (address = 0; address < 0xff; address++) {
3604763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		u8 old_value, ctrl_value;
3614763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		u8 test_value[2] = {0xff, 0xff};
3624763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
3634763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		m5602_read_sensor(sd, address, &old_value, 1);
3644763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		m5602_write_sensor(sd, address, test_value, 1);
3654763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		m5602_read_sensor(sd, address, &ctrl_value, 1);
3664763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
3674763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		if (ctrl_value == test_value[0])
3684763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén			info("register 0x%x is writeable", address);
3694763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		else
3704763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén			info("register 0x%x is read only", address);
3714763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén
3724763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		/* Restore original value */
3734763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén		m5602_write_sensor(sd, address, &old_value, 1);
3744763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén	}
3754763fa84d9942137b011629be2e7547a23cdfbc6Erik Andrén}
376