aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/sn9c102/sn9c102_mi0360.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/sn9c102/sn9c102_mi0360.c')
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0360.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c
new file mode 100644
index 000000000000..64698acb0b15
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c
@@ -0,0 +1,338 @@
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 err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
31 {0x0a, 0x14}, {0x40, 0x01},
32 {0x20, 0x17}, {0x07, 0x18},
33 {0xa0, 0x19}, {0x02, 0x1c},
34 {0x03, 0x1d}, {0x0f, 0x1e},
35 {0x0c, 0x1f}, {0x00, 0x20},
36 {0x10, 0x21}, {0x20, 0x22},
37 {0x30, 0x23}, {0x40, 0x24},
38 {0x50, 0x25}, {0x60, 0x26},
39 {0x70, 0x27}, {0x80, 0x28},
40 {0x90, 0x29}, {0xa0, 0x2a},
41 {0xb0, 0x2b}, {0xc0, 0x2c},
42 {0xd0, 0x2d}, {0xe0, 0x2e},
43 {0xf0, 0x2f}, {0xff, 0x30});
44
45 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
46 0x00, 0x01, 0, 0);
47 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
48 0x00, 0x00, 0, 0);
49 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
50 0x01, 0xe1, 0, 0);
51 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
52 0x02, 0x81, 0, 0);
53 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
54 0x00, 0x17, 0, 0);
55 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
56 0x00, 0x11, 0, 0);
57 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
58 0x04, 0x9a, 0, 0);
59
60 return err;
61}
62
63
64static int mi0360_get_ctrl(struct sn9c102_device* cam,
65 struct v4l2_control* ctrl)
66{
67 struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
68 u8 data[5+1];
69
70 switch (ctrl->id) {
71 case V4L2_CID_EXPOSURE:
72 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
73 2+1, data) < 0)
74 return -EIO;
75 ctrl->value = data[2];
76 return 0;
77 case V4L2_CID_GAIN:
78 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
79 2+1, data) < 0)
80 return -EIO;
81 ctrl->value = data[3];
82 return 0;
83 case V4L2_CID_RED_BALANCE:
84 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
85 2+1, data) < 0)
86 return -EIO;
87 ctrl->value = data[3];
88 return 0;
89 case V4L2_CID_BLUE_BALANCE:
90 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
91 2+1, data) < 0)
92 return -EIO;
93 ctrl->value = data[3];
94 return 0;
95 case SN9C102_V4L2_CID_GREEN_BALANCE:
96 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
97 2+1, data) < 0)
98 return -EIO;
99 ctrl->value = data[3];
100 return 0;
101 case V4L2_CID_HFLIP:
102 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
103 2+1, data) < 0)
104 return -EIO;
105 ctrl->value = data[3] & 0x20 ? 1 : 0;
106 return 0;
107 case V4L2_CID_VFLIP:
108 if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
109 2+1, data) < 0)
110 return -EIO;
111 ctrl->value = data[3] & 0x80 ? 1 : 0;
112 return 0;
113 default:
114 return -EINVAL;
115 }
116
117 return 0;
118}
119
120
121static int mi0360_set_ctrl(struct sn9c102_device* cam,
122 const struct v4l2_control* ctrl)
123{
124 struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
125 int err = 0;
126
127 switch (ctrl->id) {
128 case V4L2_CID_EXPOSURE:
129 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
130 0x09, ctrl->value, 0x00,
131 0, 0);
132 break;
133 case V4L2_CID_GAIN:
134 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
135 0x35, 0x03, ctrl->value,
136 0, 0);
137 break;
138 case V4L2_CID_RED_BALANCE:
139 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
140 0x2c, 0x03, ctrl->value,
141 0, 0);
142 break;
143 case V4L2_CID_BLUE_BALANCE:
144 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
145 0x2d, 0x03, ctrl->value,
146 0, 0);
147 break;
148 case SN9C102_V4L2_CID_GREEN_BALANCE:
149 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
150 0x2b, 0x03, ctrl->value,
151 0, 0);
152 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
153 0x2e, 0x03, ctrl->value,
154 0, 0);
155 break;
156 case V4L2_CID_HFLIP:
157 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
158 0x20, ctrl->value ? 0x40:0x00,
159 ctrl->value ? 0x20:0x00,
160 0, 0);
161 break;
162 case V4L2_CID_VFLIP:
163 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
164 0x20, ctrl->value ? 0x80:0x00,
165 ctrl->value ? 0x80:0x00,
166 0, 0);
167 break;
168 default:
169 return -EINVAL;
170 }
171
172 return err ? -EIO : 0;
173}
174
175
176static int mi0360_set_crop(struct sn9c102_device* cam,
177 const struct v4l2_rect* rect)
178{
179 struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
180 int err = 0;
181 u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
182 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
183
184 err += sn9c102_write_reg(cam, h_start, 0x12);
185 err += sn9c102_write_reg(cam, v_start, 0x13);
186
187 return err;
188}
189
190
191static int mi0360_set_pix_format(struct sn9c102_device* cam,
192 const struct v4l2_pix_format* pix)
193{
194 struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
195 int err = 0;
196
197 if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
198 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
199 0x0a, 0x00, 0x02, 0, 0);
200 err += sn9c102_write_reg(cam, 0x20, 0x19);
201 } else {
202 err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
203 0x0a, 0x00, 0x05, 0, 0);
204 err += sn9c102_write_reg(cam, 0x60, 0x19);
205 }
206
207 return err;
208}
209
210
211static struct sn9c102_sensor mi0360 = {
212 .name = "MI-0360",
213 .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
214 .supported_bridge = BRIDGE_SN9C103,
215 .frequency = SN9C102_I2C_100KHZ,
216 .interface = SN9C102_I2C_2WIRES,
217 .i2c_slave_id = 0x5d,
218 .init = &mi0360_init,
219 .qctrl = {
220 {
221 .id = V4L2_CID_EXPOSURE,
222 .type = V4L2_CTRL_TYPE_INTEGER,
223 .name = "exposure",
224 .minimum = 0x00,
225 .maximum = 0x0f,
226 .step = 0x01,
227 .default_value = 0x05,
228 .flags = 0,
229 },
230 {
231 .id = V4L2_CID_GAIN,
232 .type = V4L2_CTRL_TYPE_INTEGER,
233 .name = "global gain",
234 .minimum = 0x00,
235 .maximum = 0x7f,
236 .step = 0x01,
237 .default_value = 0x25,
238 .flags = 0,
239 },
240 {
241 .id = V4L2_CID_HFLIP,
242 .type = V4L2_CTRL_TYPE_BOOLEAN,
243 .name = "horizontal mirror",
244 .minimum = 0,
245 .maximum = 1,
246 .step = 1,
247 .default_value = 0,
248 .flags = 0,
249 },
250 {
251 .id = V4L2_CID_VFLIP,
252 .type = V4L2_CTRL_TYPE_BOOLEAN,
253 .name = "vertical mirror",
254 .minimum = 0,
255 .maximum = 1,
256 .step = 1,
257 .default_value = 0,
258 .flags = 0,
259 },
260 {
261 .id = V4L2_CID_BLUE_BALANCE,
262 .type = V4L2_CTRL_TYPE_INTEGER,
263 .name = "blue balance",
264 .minimum = 0x00,
265 .maximum = 0x7f,
266 .step = 0x01,
267 .default_value = 0x0f,
268 .flags = 0,
269 },
270 {
271 .id = V4L2_CID_RED_BALANCE,
272 .type = V4L2_CTRL_TYPE_INTEGER,
273 .name = "red balance",
274 .minimum = 0x00,
275 .maximum = 0x7f,
276 .step = 0x01,
277 .default_value = 0x32,
278 .flags = 0,
279 },
280 {
281 .id = SN9C102_V4L2_CID_GREEN_BALANCE,
282 .type = V4L2_CTRL_TYPE_INTEGER,
283 .name = "green balance",
284 .minimum = 0x00,
285 .maximum = 0x7f,
286 .step = 0x01,
287 .default_value = 0x25,
288 .flags = 0,
289 },
290 },
291 .get_ctrl = &mi0360_get_ctrl,
292 .set_ctrl = &mi0360_set_ctrl,
293 .cropcap = {
294 .bounds = {
295 .left = 0,
296 .top = 0,
297 .width = 640,
298 .height = 480,
299 },
300 .defrect = {
301 .left = 0,
302 .top = 0,
303 .width = 640,
304 .height = 480,
305 },
306 },
307 .set_crop = &mi0360_set_crop,
308 .pix_format = {
309 .width = 640,
310 .height = 480,
311 .pixelformat = V4L2_PIX_FMT_SBGGR8,
312 .priv = 8,
313 },
314 .set_pix_format = &mi0360_set_pix_format
315};
316
317
318int sn9c102_probe_mi0360(struct sn9c102_device* cam)
319{
320 u8 data[5+1];
321 int err;
322
323 err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
324 {0x28, 0x17});
325 if (err)
326 return -EIO;
327
328 if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00,
329 2+1, data) < 0)
330 return -EIO;
331
332 if (data[2] != 0x82 || data[3] != 0x43)
333 return -ENODEV;
334
335 sn9c102_attach_sensor(cam, &mi0360);
336
337 return 0;
338}