sn9c102_mi0343.c 9.64 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/***************************************************************************
2
 * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera     *
Linus Torvalds's avatar
Linus Torvalds committed
3 4
 * Controllers                                                             *
 *                                                                         *
5
 * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it>  *
Linus Torvalds's avatar
Linus Torvalds committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *                                                                         *
 * This program is free software; you can redistribute it and/or modify    *
 * it under the terms of the GNU General Public License as published by    *
 * the Free Software Foundation; either version 2 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software             *
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
 ***************************************************************************/

#include "sn9c102_sensor.h"


static int mi0343_init(struct sn9c102_device* cam)
{
27
	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34 35 36 37
	int err = 0;

	err += sn9c102_write_reg(cam, 0x00, 0x10);
	err += sn9c102_write_reg(cam, 0x00, 0x11);
	err += sn9c102_write_reg(cam, 0x0a, 0x14);
	err += sn9c102_write_reg(cam, 0x40, 0x01);
	err += sn9c102_write_reg(cam, 0x20, 0x17);
	err += sn9c102_write_reg(cam, 0x07, 0x18);
	err += sn9c102_write_reg(cam, 0xa0, 0x19);

38 39 40 41 42 43 44 45 46 47 48 49 50 51
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
					 0x00, 0x01, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
					 0x00, 0x00, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
					 0x01, 0xe1, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
					 0x02, 0x81, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
					 0x00, 0x17, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
					 0x00, 0x11, 0, 0);
	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
					 0x04, 0x9a, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56

	return err;
}


57 58
static int mi0343_get_ctrl(struct sn9c102_device* cam,
			   struct v4l2_control* ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
59
{
60 61 62
	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
	u8 data[5+1];

Linus Torvalds's avatar
Linus Torvalds committed
63 64
	switch (ctrl->id) {
	case V4L2_CID_EXPOSURE:
65 66
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
67
			return -EIO;
68
		ctrl->value = data[2];
Linus Torvalds's avatar
Linus Torvalds committed
69 70
		return 0;
	case V4L2_CID_GAIN:
71 72
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75
			return -EIO;
		break;
	case V4L2_CID_HFLIP:
76 77
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
78
			return -EIO;
79
		ctrl->value = data[3] & 0x20 ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
80 81
		return 0;
	case V4L2_CID_VFLIP:
82 83
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
84
			return -EIO;
85
		ctrl->value = data[3] & 0x80 ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
86 87
		return 0;
	case V4L2_CID_RED_BALANCE:
88 89
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
90 91 92
			return -EIO;
		break;
	case V4L2_CID_BLUE_BALANCE:
93 94
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
95 96 97
			return -EIO;
		break;
	case SN9C102_V4L2_CID_GREEN_BALANCE:
98 99
		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
					     2+1, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104 105 106 107 108 109 110
			return -EIO;
		break;
	default:
		return -EINVAL;
	}

	switch (ctrl->id) {
	case V4L2_CID_GAIN:
	case V4L2_CID_RED_BALANCE:
	case V4L2_CID_BLUE_BALANCE:
	case SN9C102_V4L2_CID_GREEN_BALANCE:
111
		ctrl->value = data[3] | (data[2] << 8);
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117 118 119 120 121 122 123
		if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
			ctrl->value -= 0x10;
		else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
			ctrl->value -= 0x60;
		else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff)
			ctrl->value -= 0xe0;
	}

	return 0;
}


124 125
static int mi0343_set_ctrl(struct sn9c102_device* cam,
			   const struct v4l2_control* ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
126
{
127
	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	u16 reg = 0;
	int err = 0;

	switch (ctrl->id) {
	case V4L2_CID_GAIN:
	case V4L2_CID_RED_BALANCE:
	case V4L2_CID_BLUE_BALANCE:
	case SN9C102_V4L2_CID_GREEN_BALANCE:
		if (ctrl->value <= (0x3f-0x10))
			reg = 0x10 + ctrl->value;
		else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60)))
			reg = 0x60 + (ctrl->value - (0x3f-0x10));
		else
			reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60));
		break;
	}

	switch (ctrl->id) {
	case V4L2_CID_EXPOSURE:
147
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
148 149
						 0x09, ctrl->value, 0x00,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
150 151
		break;
	case V4L2_CID_GAIN:
152
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
153 154
						 0x35, reg >> 8, reg & 0xff,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
155 156
		break;
	case V4L2_CID_HFLIP:
157
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
158 159 160
						 0x20, ctrl->value ? 0x40:0x00,
						 ctrl->value ? 0x20:0x00,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
161 162
		break;
	case V4L2_CID_VFLIP:
163
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
164 165 166
						 0x20, ctrl->value ? 0x80:0x00,
						 ctrl->value ? 0x80:0x00,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
167 168
		break;
	case V4L2_CID_RED_BALANCE:
169
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
170 171
						 0x2d, reg >> 8, reg & 0xff,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
172 173
		break;
	case V4L2_CID_BLUE_BALANCE:
174
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
175 176
						 0x2c, reg >> 8, reg & 0xff,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
177 178
		break;
	case SN9C102_V4L2_CID_GREEN_BALANCE:
179
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
180 181
						 0x2b, reg >> 8, reg & 0xff,
						 0, 0);
182
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
183 184
						 0x2e, reg >> 8, reg & 0xff,
						 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188 189 190 191 192 193
		break;
	default:
		return -EINVAL;
	}

	return err ? -EIO : 0;
}


194 195
static int mi0343_set_crop(struct sn9c102_device* cam,
			    const struct v4l2_rect* rect)
Linus Torvalds's avatar
Linus Torvalds committed
196
{
197
	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200 201 202 203 204 205 206 207 208
	int err = 0;
	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;

	err += sn9c102_write_reg(cam, h_start, 0x12);
	err += sn9c102_write_reg(cam, v_start, 0x13);

	return err;
}


209 210
static int mi0343_set_pix_format(struct sn9c102_device* cam,
				 const struct v4l2_pix_format* pix)
Linus Torvalds's avatar
Linus Torvalds committed
211
{
212
	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215
	int err = 0;

	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
216
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
217
						 0x0a, 0x00, 0x03, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
218 219
		err += sn9c102_write_reg(cam, 0x20, 0x19);
	} else {
220
		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
221
						 0x0a, 0x00, 0x05, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
222 223 224 225 226 227 228 229 230 231
		err += sn9c102_write_reg(cam, 0xa0, 0x19);
	}

	return err;
}


static struct sn9c102_sensor mi0343 = {
	.name = "MI-0343",
	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
232
	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
	.frequency = SN9C102_I2C_100KHZ,
	.interface = SN9C102_I2C_2WIRES,
	.i2c_slave_id = 0x5d,
	.init = &mi0343_init,
	.qctrl = {
		{
			.id = V4L2_CID_EXPOSURE,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "exposure",
			.minimum = 0x00,
			.maximum = 0x0f,
			.step = 0x01,
			.default_value = 0x06,
			.flags = 0,
		},
		{
			.id = V4L2_CID_GAIN,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "global gain",
			.minimum = 0x00,
			.maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/
			.step = 0x01,
			.default_value = 0x00,
			.flags = 0,
		},
		{
			.id = V4L2_CID_HFLIP,
			.type = V4L2_CTRL_TYPE_BOOLEAN,
			.name = "horizontal mirror",
			.minimum = 0,
			.maximum = 1,
			.step = 1,
			.default_value = 0,
			.flags = 0,
		},
		{
			.id = V4L2_CID_VFLIP,
			.type = V4L2_CTRL_TYPE_BOOLEAN,
			.name = "vertical mirror",
			.minimum = 0,
			.maximum = 1,
			.step = 1,
			.default_value = 0,
			.flags = 0,
		},
		{
			.id = V4L2_CID_RED_BALANCE,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "red balance",
			.minimum = 0x00,
			.maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
			.step = 0x01,
			.default_value = 0x00,
			.flags = 0,
		},
		{
			.id = V4L2_CID_BLUE_BALANCE,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "blue balance",
			.minimum = 0x00,
			.maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
			.step = 0x01,
			.default_value = 0x00,
			.flags = 0,
		},
		{
			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
			.type = V4L2_CTRL_TYPE_INTEGER,
			.name = "green balance",
			.minimum = 0x00,
			.maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)),
			.step = 0x01,
			.default_value = 0x00,
			.flags = 0,
		},
	},
	.get_ctrl = &mi0343_get_ctrl,
	.set_ctrl = &mi0343_set_ctrl,
	.cropcap = {
		.bounds = {
			.left = 0,
			.top = 0,
			.width = 640,
			.height = 480,
		},
		.defrect = {
			.left = 0,
			.top = 0,
			.width = 640,
			.height = 480,
		},
	},
	.set_crop = &mi0343_set_crop,
	.pix_format = {
		.width = 640,
		.height = 480,
		.pixelformat = V4L2_PIX_FMT_SBGGR8,
		.priv = 8,
	},
	.set_pix_format = &mi0343_set_pix_format
};


int sn9c102_probe_mi0343(struct sn9c102_device* cam)
{
338
	u8 data[5+1];
Linus Torvalds's avatar
Linus Torvalds committed
339 340 341 342 343 344 345 346 347
	int err = 0;

	err += sn9c102_write_reg(cam, 0x01, 0x01);
	err += sn9c102_write_reg(cam, 0x00, 0x01);
	err += sn9c102_write_reg(cam, 0x28, 0x17);
	if (err)
		return -EIO;

	if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
348
				     2, data) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
349 350
		return -EIO;

351
	if (data[4] != 0x32 || data[3] != 0xe3)
Linus Torvalds's avatar
Linus Torvalds committed
352 353 354 355 356 357
		return -ENODEV;

	sn9c102_attach_sensor(cam, &mi0343);

	return 0;
}