diff options
Diffstat (limited to 'drivers/media/video/sn9c102/sn9c102_mi0360.c')
-rw-r--r-- | drivers/media/video/sn9c102/sn9c102_mi0360.c | 338 |
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 | |||
25 | static 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 | |||
64 | static 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 | |||
121 | static 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 | |||
176 | static 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 | |||
191 | static 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 | |||
211 | static 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 | |||
318 | int 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 | } | ||