1/***************************************************************************
2 * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera     *
3 * Controllers                                                             *
4 *                                                                         *
5 * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it>       *
6 *                                                                         *
7 * This program is free software; you can redistribute it and/or modify    *
8 * it under the terms of the GNU General Public License as published by    *
9 * the Free Software Foundation; either version 2 of the License, or       *
10 * (at your option) any later version.                                     *
11 *                                                                         *
12 * This program is distributed in the hope that it will be useful,         *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
15 * GNU General Public License for more details.                            *
16 *                                                                         *
17 * You should have received a copy of the GNU General Public License       *
18 * along with this program; if not, write to the Free Software             *
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
20 ***************************************************************************/
21
22#include "sn9c102_sensor.h"
23
24
25static int mi0360_init(struct sn9c102_device* cam)
26{
27	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
28	int err = 0;
29
30	switch (sn9c102_get_bridge(cam)) {
31	case BRIDGE_SN9C103:
32		err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
33					       {0x0a, 0x14}, {0x40, 0x01},
34					       {0x20, 0x17}, {0x07, 0x18},
35					       {0xa0, 0x19}, {0x02, 0x1c},
36					       {0x03, 0x1d}, {0x0f, 0x1e},
37					       {0x0c, 0x1f}, {0x00, 0x20},
38					       {0x10, 0x21}, {0x20, 0x22},
39					       {0x30, 0x23}, {0x40, 0x24},
40					       {0x50, 0x25}, {0x60, 0x26},
41					       {0x70, 0x27}, {0x80, 0x28},
42					       {0x90, 0x29}, {0xa0, 0x2a},
43					       {0xb0, 0x2b}, {0xc0, 0x2c},
44					       {0xd0, 0x2d}, {0xe0, 0x2e},
45					       {0xf0, 0x2f}, {0xff, 0x30});
46		break;
47	case BRIDGE_SN9C105:
48	case BRIDGE_SN9C120:
49		err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
50					       {0x00, 0x03}, {0x1a, 0x04},
51					       {0x50, 0x05}, {0x20, 0x06},
52					       {0x10, 0x07}, {0x03, 0x10},
53					       {0x08, 0x14}, {0xa2, 0x17},
54					       {0x47, 0x18}, {0x00, 0x19},
55					       {0x1d, 0x1a}, {0x10, 0x1b},
56					       {0x02, 0x1c}, {0x03, 0x1d},
57					       {0x0f, 0x1e}, {0x0c, 0x1f},
58					       {0x00, 0x20}, {0x29, 0x21},
59					       {0x40, 0x22}, {0x54, 0x23},
60					       {0x66, 0x24}, {0x76, 0x25},
61					       {0x85, 0x26}, {0x94, 0x27},
62					       {0xa1, 0x28}, {0xae, 0x29},
63					       {0xbb, 0x2a}, {0xc7, 0x2b},
64					       {0xd3, 0x2c}, {0xde, 0x2d},
65					       {0xea, 0x2e}, {0xf4, 0x2f},
66					       {0xff, 0x30}, {0x00, 0x3F},
67					       {0xC7, 0x40}, {0x01, 0x41},
68					       {0x44, 0x42}, {0x00, 0x43},
69					       {0x44, 0x44}, {0x00, 0x45},
70					       {0x44, 0x46}, {0x00, 0x47},
71					       {0xC7, 0x48}, {0x01, 0x49},
72					       {0xC7, 0x4A}, {0x01, 0x4B},
73					       {0xC7, 0x4C}, {0x01, 0x4D},
74					       {0x44, 0x4E}, {0x00, 0x4F},
75					       {0x44, 0x50}, {0x00, 0x51},
76					       {0x44, 0x52}, {0x00, 0x53},
77					       {0xC7, 0x54}, {0x01, 0x55},
78					       {0xC7, 0x56}, {0x01, 0x57},
79					       {0xC7, 0x58}, {0x01, 0x59},
80					       {0x44, 0x5A}, {0x00, 0x5B},
81					       {0x44, 0x5C}, {0x00, 0x5D},
82					       {0x44, 0x5E}, {0x00, 0x5F},
83					       {0xC7, 0x60}, {0x01, 0x61},
84					       {0xC7, 0x62}, {0x01, 0x63},
85					       {0xC7, 0x64}, {0x01, 0x65},
86					       {0x44, 0x66}, {0x00, 0x67},
87					       {0x44, 0x68}, {0x00, 0x69},
88					       {0x44, 0x6A}, {0x00, 0x6B},
89					       {0xC7, 0x6C}, {0x01, 0x6D},
90					       {0xC7, 0x6E}, {0x01, 0x6F},
91					       {0xC7, 0x70}, {0x01, 0x71},
92					       {0x44, 0x72}, {0x00, 0x73},
93					       {0x44, 0x74}, {0x00, 0x75},
94					       {0x44, 0x76}, {0x00, 0x77},
95					       {0xC7, 0x78}, {0x01, 0x79},
96					       {0xC7, 0x7A}, {0x01, 0x7B},
97					       {0xC7, 0x7C}, {0x01, 0x7D},
98					       {0x44, 0x7E}, {0x00, 0x7F},
99					       {0x14, 0x84}, {0x00, 0x85},
100					       {0x27, 0x86}, {0x00, 0x87},
101					       {0x07, 0x88}, {0x00, 0x89},
102					       {0xEC, 0x8A}, {0x0f, 0x8B},
103					       {0xD8, 0x8C}, {0x0f, 0x8D},
104					       {0x3D, 0x8E}, {0x00, 0x8F},
105					       {0x3D, 0x90}, {0x00, 0x91},
106					       {0xCD, 0x92}, {0x0f, 0x93},
107					       {0xf7, 0x94}, {0x0f, 0x95},
108					       {0x0C, 0x96}, {0x00, 0x97},
109					       {0x00, 0x98}, {0x66, 0x99},
110					       {0x05, 0x9A}, {0x00, 0x9B},
111					       {0x04, 0x9C}, {0x00, 0x9D},
112					       {0x08, 0x9E}, {0x00, 0x9F},
113					       {0x2D, 0xC0}, {0x2D, 0xC1},
114					       {0x3A, 0xC2}, {0x05, 0xC3},
115					       {0x04, 0xC4}, {0x3F, 0xC5},
116					       {0x00, 0xC6}, {0x00, 0xC7},
117					       {0x50, 0xC8}, {0x3C, 0xC9},
118					       {0x28, 0xCA}, {0xD8, 0xCB},
119					       {0x14, 0xCC}, {0xEC, 0xCD},
120					       {0x32, 0xCE}, {0xDD, 0xCF},
121					       {0x32, 0xD0}, {0xDD, 0xD1},
122					       {0x6A, 0xD2}, {0x50, 0xD3},
123					       {0x00, 0xD4}, {0x00, 0xD5},
124					       {0x00, 0xD6});
125		break;
126	default:
127		break;
128	}
129
130	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
131					 0x00, 0x01, 0, 0);
132	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
133					 0x00, 0x00, 0, 0);
134	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
135					 0x01, 0xe1, 0, 0);
136	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
137					 0x02, 0x81, 0, 0);
138	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
139					 0x00, 0x17, 0, 0);
140	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
141					 0x00, 0x11, 0, 0);
142	err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
143					 0x04, 0x9a, 0, 0);
144
145	return err;
146}
147
148
149static int mi0360_get_ctrl(struct sn9c102_device* cam,
150			   struct v4l2_control* ctrl)
151{
152	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
153	u8 data[2];
154
155	switch (ctrl->id) {
156	case V4L2_CID_EXPOSURE:
157		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2,
158					     data) < 0)
159			return -EIO;
160		ctrl->value = data[0];
161		return 0;
162	case V4L2_CID_GAIN:
163		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2,
164					     data) < 0)
165			return -EIO;
166		ctrl->value = data[1];
167		return 0;
168	case V4L2_CID_RED_BALANCE:
169		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2,
170					     data) < 0)
171			return -EIO;
172		ctrl->value = data[1];
173		return 0;
174	case V4L2_CID_BLUE_BALANCE:
175		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2,
176					     data) < 0)
177			return -EIO;
178		ctrl->value = data[1];
179		return 0;
180	case SN9C102_V4L2_CID_GREEN_BALANCE:
181		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2,
182					     data) < 0)
183			return -EIO;
184		ctrl->value = data[1];
185		return 0;
186	case V4L2_CID_HFLIP:
187		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
188					     data) < 0)
189			return -EIO;
190		ctrl->value = data[1] & 0x20 ? 1 : 0;
191		return 0;
192	case V4L2_CID_VFLIP:
193		if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
194					     data) < 0)
195			return -EIO;
196		ctrl->value = data[1] & 0x80 ? 1 : 0;
197		return 0;
198	default:
199		return -EINVAL;
200	}
201
202	return 0;
203}
204
205
206static int mi0360_set_ctrl(struct sn9c102_device* cam,
207			   const struct v4l2_control* ctrl)
208{
209	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
210	int err = 0;
211
212	switch (ctrl->id) {
213	case V4L2_CID_EXPOSURE:
214		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
215						 0x09, ctrl->value, 0x00,
216						 0, 0);
217		break;
218	case V4L2_CID_GAIN:
219		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
220						 0x35, 0x03, ctrl->value,
221						 0, 0);
222		break;
223	case V4L2_CID_RED_BALANCE:
224		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
225						 0x2c, 0x03, ctrl->value,
226						 0, 0);
227		break;
228	case V4L2_CID_BLUE_BALANCE:
229		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
230						 0x2d, 0x03, ctrl->value,
231						 0, 0);
232		break;
233	case SN9C102_V4L2_CID_GREEN_BALANCE:
234		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
235						 0x2b, 0x03, ctrl->value,
236						 0, 0);
237		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
238						 0x2e, 0x03, ctrl->value,
239						 0, 0);
240		break;
241	case V4L2_CID_HFLIP:
242		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
243						 0x20, ctrl->value ? 0x40:0x00,
244						 ctrl->value ? 0x20:0x00,
245						 0, 0);
246		break;
247	case V4L2_CID_VFLIP:
248		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
249						 0x20, ctrl->value ? 0x80:0x00,
250						 ctrl->value ? 0x80:0x00,
251						 0, 0);
252		break;
253	default:
254		return -EINVAL;
255	}
256
257	return err ? -EIO : 0;
258}
259
260
261static int mi0360_set_crop(struct sn9c102_device* cam,
262			    const struct v4l2_rect* rect)
263{
264	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
265	int err = 0;
266	u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
267
268	switch (sn9c102_get_bridge(cam)) {
269	case BRIDGE_SN9C103:
270		h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0;
271		break;
272	case BRIDGE_SN9C105:
273	case BRIDGE_SN9C120:
274		h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1;
275		break;
276	default:
277		break;
278	}
279
280	err += sn9c102_write_reg(cam, h_start, 0x12);
281	err += sn9c102_write_reg(cam, v_start, 0x13);
282
283	return err;
284}
285
286
287static int mi0360_set_pix_format(struct sn9c102_device* cam,
288				 const struct v4l2_pix_format* pix)
289{
290	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
291	int err = 0;
292
293	if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
294		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
295						 0x0a, 0x00, 0x05, 0, 0);
296		err += sn9c102_write_reg(cam, 0x60, 0x19);
297		if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 ||
298		    sn9c102_get_bridge(cam) == BRIDGE_SN9C120)
299			err += sn9c102_write_reg(cam, 0xa6, 0x17);
300	} else {
301		err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
302						 0x0a, 0x00, 0x02, 0, 0);
303		err += sn9c102_write_reg(cam, 0x20, 0x19);
304		if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 ||
305		    sn9c102_get_bridge(cam) == BRIDGE_SN9C120)
306			err += sn9c102_write_reg(cam, 0xa2, 0x17);
307	}
308
309	return err;
310}
311
312
313static const struct sn9c102_sensor mi0360 = {
314	.name = "MI-0360",
315	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
316	.supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120,
317	.frequency = SN9C102_I2C_100KHZ,
318	.interface = SN9C102_I2C_2WIRES,
319	.i2c_slave_id = 0x5d,
320	.init = &mi0360_init,
321	.qctrl = {
322		{
323			.id = V4L2_CID_EXPOSURE,
324			.type = V4L2_CTRL_TYPE_INTEGER,
325			.name = "exposure",
326			.minimum = 0x00,
327			.maximum = 0x0f,
328			.step = 0x01,
329			.default_value = 0x05,
330			.flags = 0,
331		},
332		{
333			.id = V4L2_CID_GAIN,
334			.type = V4L2_CTRL_TYPE_INTEGER,
335			.name = "global gain",
336			.minimum = 0x00,
337			.maximum = 0x7f,
338			.step = 0x01,
339			.default_value = 0x25,
340			.flags = 0,
341		},
342		{
343			.id = V4L2_CID_HFLIP,
344			.type = V4L2_CTRL_TYPE_BOOLEAN,
345			.name = "horizontal mirror",
346			.minimum = 0,
347			.maximum = 1,
348			.step = 1,
349			.default_value = 0,
350			.flags = 0,
351		},
352		{
353			.id = V4L2_CID_VFLIP,
354			.type = V4L2_CTRL_TYPE_BOOLEAN,
355			.name = "vertical mirror",
356			.minimum = 0,
357			.maximum = 1,
358			.step = 1,
359			.default_value = 0,
360			.flags = 0,
361		},
362		{
363			.id = V4L2_CID_BLUE_BALANCE,
364			.type = V4L2_CTRL_TYPE_INTEGER,
365			.name = "blue balance",
366			.minimum = 0x00,
367			.maximum = 0x7f,
368			.step = 0x01,
369			.default_value = 0x0f,
370			.flags = 0,
371		},
372		{
373			.id = V4L2_CID_RED_BALANCE,
374			.type = V4L2_CTRL_TYPE_INTEGER,
375			.name = "red balance",
376			.minimum = 0x00,
377			.maximum = 0x7f,
378			.step = 0x01,
379			.default_value = 0x32,
380			.flags = 0,
381		},
382		{
383			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
384			.type = V4L2_CTRL_TYPE_INTEGER,
385			.name = "green balance",
386			.minimum = 0x00,
387			.maximum = 0x7f,
388			.step = 0x01,
389			.default_value = 0x25,
390			.flags = 0,
391		},
392	},
393	.get_ctrl = &mi0360_get_ctrl,
394	.set_ctrl = &mi0360_set_ctrl,
395	.cropcap = {
396		.bounds = {
397			.left = 0,
398			.top = 0,
399			.width = 640,
400			.height = 480,
401		},
402		.defrect = {
403			.left = 0,
404			.top = 0,
405			.width = 640,
406			.height = 480,
407		},
408	},
409	.set_crop = &mi0360_set_crop,
410	.pix_format = {
411		.width = 640,
412		.height = 480,
413		.pixelformat = V4L2_PIX_FMT_SBGGR8,
414		.priv = 8,
415	},
416	.set_pix_format = &mi0360_set_pix_format
417};
418
419
420int sn9c102_probe_mi0360(struct sn9c102_device* cam)
421{
422
423	u8 data[2];
424
425	switch (sn9c102_get_bridge(cam)) {
426	case BRIDGE_SN9C103:
427		if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
428					     {0x28, 0x17}))
429			return -EIO;
430		break;
431	case BRIDGE_SN9C105:
432	case BRIDGE_SN9C120:
433		if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
434					     {0x01, 0x01}, {0x00, 0x01},
435					     {0x28, 0x17}))
436			return -EIO;
437		break;
438	default:
439		break;
440	}
441
442	if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00,
443				     2, data) < 0)
444		return -EIO;
445
446	if (data[0] != 0x82 || data[1] != 0x43)
447		return -ENODEV;
448
449	sn9c102_attach_sensor(cam, &mi0360);
450
451	return 0;
452}
453