1 /***************************************************************************
2  * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera     *
3  * Controllers                                                             *
4  *                                                                         *
5  * Copyright (C) 2004-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 <linux/delay.h>
23 #include "sn9c102_sensor.h"
24 #include "sn9c102_devtable.h"
25 
26 
pas106b_init(struct sn9c102_device * cam)27 static int pas106b_init(struct sn9c102_device* cam)
28 {
29 	int err = 0;
30 
31 	err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
32 				       {0x00, 0x14}, {0x20, 0x17},
33 				       {0x20, 0x19}, {0x09, 0x18});
34 
35 	err += sn9c102_i2c_write(cam, 0x02, 0x0c);
36 	err += sn9c102_i2c_write(cam, 0x05, 0x5a);
37 	err += sn9c102_i2c_write(cam, 0x06, 0x88);
38 	err += sn9c102_i2c_write(cam, 0x07, 0x80);
39 	err += sn9c102_i2c_write(cam, 0x10, 0x06);
40 	err += sn9c102_i2c_write(cam, 0x11, 0x06);
41 	err += sn9c102_i2c_write(cam, 0x12, 0x00);
42 	err += sn9c102_i2c_write(cam, 0x14, 0x02);
43 	err += sn9c102_i2c_write(cam, 0x13, 0x01);
44 
45 	msleep(400);
46 
47 	return err;
48 }
49 
50 
pas106b_get_ctrl(struct sn9c102_device * cam,struct v4l2_control * ctrl)51 static int pas106b_get_ctrl(struct sn9c102_device* cam,
52 			    struct v4l2_control* ctrl)
53 {
54 	switch (ctrl->id) {
55 	case V4L2_CID_EXPOSURE:
56 		{
57 			int r1 = sn9c102_i2c_read(cam, 0x03),
58 			    r2 = sn9c102_i2c_read(cam, 0x04);
59 			if (r1 < 0 || r2 < 0)
60 				return -EIO;
61 			ctrl->value = (r1 << 4) | (r2 & 0x0f);
62 		}
63 		return 0;
64 	case V4L2_CID_RED_BALANCE:
65 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
66 			return -EIO;
67 		ctrl->value &= 0x1f;
68 		return 0;
69 	case V4L2_CID_BLUE_BALANCE:
70 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
71 			return -EIO;
72 		ctrl->value &= 0x1f;
73 		return 0;
74 	case V4L2_CID_GAIN:
75 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0)
76 			return -EIO;
77 		ctrl->value &= 0x1f;
78 		return 0;
79 	case V4L2_CID_CONTRAST:
80 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
81 			return -EIO;
82 		ctrl->value &= 0x07;
83 		return 0;
84 	case SN9C102_V4L2_CID_GREEN_BALANCE:
85 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0)
86 			return -EIO;
87 		ctrl->value = (ctrl->value & 0x1f) << 1;
88 		return 0;
89 	case SN9C102_V4L2_CID_DAC_MAGNITUDE:
90 		if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
91 			return -EIO;
92 		ctrl->value &= 0xf8;
93 		return 0;
94 	default:
95 		return -EINVAL;
96 	}
97 }
98 
99 
pas106b_set_ctrl(struct sn9c102_device * cam,const struct v4l2_control * ctrl)100 static int pas106b_set_ctrl(struct sn9c102_device* cam,
101 			    const struct v4l2_control* ctrl)
102 {
103 	int err = 0;
104 
105 	switch (ctrl->id) {
106 	case V4L2_CID_EXPOSURE:
107 		err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4);
108 		err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f);
109 		break;
110 	case V4L2_CID_RED_BALANCE:
111 		err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
112 		break;
113 	case V4L2_CID_BLUE_BALANCE:
114 		err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
115 		break;
116 	case V4L2_CID_GAIN:
117 		err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
118 		break;
119 	case V4L2_CID_CONTRAST:
120 		err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
121 		break;
122 	case SN9C102_V4L2_CID_GREEN_BALANCE:
123 		err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1);
124 		err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1);
125 		break;
126 	case SN9C102_V4L2_CID_DAC_MAGNITUDE:
127 		err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3);
128 		break;
129 	default:
130 		return -EINVAL;
131 	}
132 	err += sn9c102_i2c_write(cam, 0x13, 0x01);
133 
134 	return err ? -EIO : 0;
135 }
136 
137 
pas106b_set_crop(struct sn9c102_device * cam,const struct v4l2_rect * rect)138 static int pas106b_set_crop(struct sn9c102_device* cam,
139 			    const struct v4l2_rect* rect)
140 {
141 	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
142 	int err = 0;
143 	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
144 	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
145 
146 	err += sn9c102_write_reg(cam, h_start, 0x12);
147 	err += sn9c102_write_reg(cam, v_start, 0x13);
148 
149 	return err;
150 }
151 
152 
pas106b_set_pix_format(struct sn9c102_device * cam,const struct v4l2_pix_format * pix)153 static int pas106b_set_pix_format(struct sn9c102_device* cam,
154 				  const struct v4l2_pix_format* pix)
155 {
156 	int err = 0;
157 
158 	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
159 		err += sn9c102_write_reg(cam, 0x2c, 0x17);
160 	else
161 		err += sn9c102_write_reg(cam, 0x20, 0x17);
162 
163 	return err;
164 }
165 
166 
167 static const struct sn9c102_sensor pas106b = {
168 	.name = "PAS106B",
169 	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
170 	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
171 	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
172 	.frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
173 	.interface = SN9C102_I2C_2WIRES,
174 	.i2c_slave_id = 0x40,
175 	.init = &pas106b_init,
176 	.qctrl = {
177 		{
178 			.id = V4L2_CID_EXPOSURE,
179 			.type = V4L2_CTRL_TYPE_INTEGER,
180 			.name = "exposure",
181 			.minimum = 0x125,
182 			.maximum = 0xfff,
183 			.step = 0x001,
184 			.default_value = 0x140,
185 			.flags = 0,
186 		},
187 		{
188 			.id = V4L2_CID_GAIN,
189 			.type = V4L2_CTRL_TYPE_INTEGER,
190 			.name = "global gain",
191 			.minimum = 0x00,
192 			.maximum = 0x1f,
193 			.step = 0x01,
194 			.default_value = 0x0d,
195 			.flags = 0,
196 		},
197 		{
198 			.id = V4L2_CID_CONTRAST,
199 			.type = V4L2_CTRL_TYPE_INTEGER,
200 			.name = "contrast",
201 			.minimum = 0x00,
202 			.maximum = 0x07,
203 			.step = 0x01,
204 			.default_value = 0x00, /* 0x00~0x03 have same effect */
205 			.flags = 0,
206 		},
207 		{
208 			.id = V4L2_CID_RED_BALANCE,
209 			.type = V4L2_CTRL_TYPE_INTEGER,
210 			.name = "red balance",
211 			.minimum = 0x00,
212 			.maximum = 0x1f,
213 			.step = 0x01,
214 			.default_value = 0x04,
215 			.flags = 0,
216 		},
217 		{
218 			.id = V4L2_CID_BLUE_BALANCE,
219 			.type = V4L2_CTRL_TYPE_INTEGER,
220 			.name = "blue balance",
221 			.minimum = 0x00,
222 			.maximum = 0x1f,
223 			.step = 0x01,
224 			.default_value = 0x06,
225 			.flags = 0,
226 		},
227 		{
228 			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
229 			.type = V4L2_CTRL_TYPE_INTEGER,
230 			.name = "green balance",
231 			.minimum = 0x00,
232 			.maximum = 0x3e,
233 			.step = 0x02,
234 			.default_value = 0x02,
235 			.flags = 0,
236 		},
237 		{
238 			.id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
239 			.type = V4L2_CTRL_TYPE_INTEGER,
240 			.name = "DAC magnitude",
241 			.minimum = 0x00,
242 			.maximum = 0x1f,
243 			.step = 0x01,
244 			.default_value = 0x01,
245 			.flags = 0,
246 		},
247 	},
248 	.get_ctrl = &pas106b_get_ctrl,
249 	.set_ctrl = &pas106b_set_ctrl,
250 	.cropcap = {
251 		.bounds = {
252 			.left = 0,
253 			.top = 0,
254 			.width = 352,
255 			.height = 288,
256 		},
257 		.defrect = {
258 			.left = 0,
259 			.top = 0,
260 			.width = 352,
261 			.height = 288,
262 		},
263 	},
264 	.set_crop = &pas106b_set_crop,
265 	.pix_format = {
266 		.width = 352,
267 		.height = 288,
268 		.pixelformat = V4L2_PIX_FMT_SBGGR8,
269 		.priv = 8, /* we use this field as 'bits per pixel' */
270 	},
271 	.set_pix_format = &pas106b_set_pix_format
272 };
273 
274 
sn9c102_probe_pas106b(struct sn9c102_device * cam)275 int sn9c102_probe_pas106b(struct sn9c102_device* cam)
276 {
277 	int r0 = 0, r1 = 0;
278 	unsigned int pid = 0;
279 
280 	/*
281 	   Minimal initialization to enable the I2C communication
282 	   NOTE: do NOT change the values!
283 	*/
284 	if (sn9c102_write_const_regs(cam,
285 				     {0x01, 0x01}, /* sensor power down */
286 				     {0x00, 0x01}, /* sensor power on */
287 				    {0x28, 0x17})) /* sensor clock at 24 MHz */
288 		return -EIO;
289 
290 	r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
291 	r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
292 	if (r0 < 0 || r1 < 0)
293 		return -EIO;
294 
295 	pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
296 	if (pid != 0x007)
297 		return -ENODEV;
298 
299 	sn9c102_attach_sensor(cam, &pas106b);
300 
301 	return 0;
302 }
303