Commit | Line | Data |
---|---|---|
b9df978f | 1 | /*************************************************************************** |
f327ebbd | 2 | * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * |
b9df978f LR |
3 | * Controllers * |
4 | * * | |
f327ebbd | 5 | * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * |
b9df978f LR |
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 | ||
b9df978f LR |
25 | static int ov7630_init(struct sn9c102_device* cam) |
26 | { | |
27 | int err = 0; | |
28 | ||
f327ebbd LR |
29 | switch (sn9c102_get_bridge(cam)) { |
30 | case BRIDGE_SN9C101: | |
31 | case BRIDGE_SN9C102: | |
f423b9a8 LR |
32 | err += sn9c102_write_reg(cam, 0x00, 0x14); |
33 | err += sn9c102_write_reg(cam, 0x60, 0x17); | |
34 | err += sn9c102_write_reg(cam, 0x0f, 0x18); | |
35 | err += sn9c102_write_reg(cam, 0x50, 0x19); | |
b9df978f | 36 | |
f327ebbd LR |
37 | err += sn9c102_i2c_write(cam, 0x12, 0x8d); |
38 | err += sn9c102_i2c_write(cam, 0x12, 0x0d); | |
39 | err += sn9c102_i2c_write(cam, 0x11, 0x00); | |
f423b9a8 LR |
40 | err += sn9c102_i2c_write(cam, 0x15, 0x35); |
41 | err += sn9c102_i2c_write(cam, 0x16, 0x03); | |
42 | err += sn9c102_i2c_write(cam, 0x17, 0x1c); | |
43 | err += sn9c102_i2c_write(cam, 0x18, 0xbd); | |
44 | err += sn9c102_i2c_write(cam, 0x19, 0x06); | |
45 | err += sn9c102_i2c_write(cam, 0x1a, 0xf6); | |
46 | err += sn9c102_i2c_write(cam, 0x1b, 0x04); | |
f327ebbd LR |
47 | err += sn9c102_i2c_write(cam, 0x20, 0x44); |
48 | err += sn9c102_i2c_write(cam, 0x23, 0xee); | |
49 | err += sn9c102_i2c_write(cam, 0x26, 0xa0); | |
50 | err += sn9c102_i2c_write(cam, 0x27, 0x9a); | |
51 | err += sn9c102_i2c_write(cam, 0x28, 0x20); | |
52 | err += sn9c102_i2c_write(cam, 0x29, 0x30); | |
53 | err += sn9c102_i2c_write(cam, 0x2f, 0x3d); | |
54 | err += sn9c102_i2c_write(cam, 0x30, 0x24); | |
55 | err += sn9c102_i2c_write(cam, 0x32, 0x86); | |
56 | err += sn9c102_i2c_write(cam, 0x60, 0xa9); | |
57 | err += sn9c102_i2c_write(cam, 0x61, 0x42); | |
58 | err += sn9c102_i2c_write(cam, 0x65, 0x00); | |
59 | err += sn9c102_i2c_write(cam, 0x69, 0x38); | |
60 | err += sn9c102_i2c_write(cam, 0x6f, 0x88); | |
61 | err += sn9c102_i2c_write(cam, 0x70, 0x0b); | |
62 | err += sn9c102_i2c_write(cam, 0x71, 0x00); | |
63 | err += sn9c102_i2c_write(cam, 0x74, 0x21); | |
64 | err += sn9c102_i2c_write(cam, 0x7d, 0xf7); | |
65 | break; | |
66 | case BRIDGE_SN9C103: | |
67 | err += sn9c102_write_reg(cam, 0x00, 0x02); | |
68 | err += sn9c102_write_reg(cam, 0x00, 0x03); | |
69 | err += sn9c102_write_reg(cam, 0x1a, 0x04); | |
70 | err += sn9c102_write_reg(cam, 0x20, 0x05); | |
71 | err += sn9c102_write_reg(cam, 0x20, 0x06); | |
72 | err += sn9c102_write_reg(cam, 0x20, 0x07); | |
73 | err += sn9c102_write_reg(cam, 0x03, 0x10); | |
74 | err += sn9c102_write_reg(cam, 0x0a, 0x14); | |
75 | err += sn9c102_write_reg(cam, 0x60, 0x17); | |
76 | err += sn9c102_write_reg(cam, 0x0f, 0x18); | |
77 | err += sn9c102_write_reg(cam, 0x50, 0x19); | |
78 | err += sn9c102_write_reg(cam, 0x1d, 0x1a); | |
79 | err += sn9c102_write_reg(cam, 0x10, 0x1b); | |
80 | err += sn9c102_write_reg(cam, 0x02, 0x1c); | |
81 | err += sn9c102_write_reg(cam, 0x03, 0x1d); | |
82 | err += sn9c102_write_reg(cam, 0x0f, 0x1e); | |
83 | err += sn9c102_write_reg(cam, 0x0c, 0x1f); | |
84 | err += sn9c102_write_reg(cam, 0x00, 0x20); | |
85 | err += sn9c102_write_reg(cam, 0x10, 0x21); | |
86 | err += sn9c102_write_reg(cam, 0x20, 0x22); | |
87 | err += sn9c102_write_reg(cam, 0x30, 0x23); | |
88 | err += sn9c102_write_reg(cam, 0x40, 0x24); | |
89 | err += sn9c102_write_reg(cam, 0x50, 0x25); | |
90 | err += sn9c102_write_reg(cam, 0x60, 0x26); | |
91 | err += sn9c102_write_reg(cam, 0x70, 0x27); | |
92 | err += sn9c102_write_reg(cam, 0x80, 0x28); | |
93 | err += sn9c102_write_reg(cam, 0x90, 0x29); | |
94 | err += sn9c102_write_reg(cam, 0xa0, 0x2a); | |
95 | err += sn9c102_write_reg(cam, 0xb0, 0x2b); | |
96 | err += sn9c102_write_reg(cam, 0xc0, 0x2c); | |
97 | err += sn9c102_write_reg(cam, 0xd0, 0x2d); | |
98 | err += sn9c102_write_reg(cam, 0xe0, 0x2e); | |
99 | err += sn9c102_write_reg(cam, 0xf0, 0x2f); | |
100 | err += sn9c102_write_reg(cam, 0xff, 0x30); | |
101 | ||
102 | err += sn9c102_i2c_write(cam, 0x12, 0x8d); | |
103 | err += sn9c102_i2c_write(cam, 0x12, 0x0d); | |
104 | err += sn9c102_i2c_write(cam, 0x15, 0x34); | |
105 | err += sn9c102_i2c_write(cam, 0x11, 0x01); | |
106 | err += sn9c102_i2c_write(cam, 0x1b, 0x04); | |
107 | err += sn9c102_i2c_write(cam, 0x20, 0x44); | |
f423b9a8 LR |
108 | err += sn9c102_i2c_write(cam, 0x23, 0xee); |
109 | err += sn9c102_i2c_write(cam, 0x26, 0xa0); | |
110 | err += sn9c102_i2c_write(cam, 0x27, 0x9a); | |
f327ebbd | 111 | err += sn9c102_i2c_write(cam, 0x28, 0x20); |
f423b9a8 LR |
112 | err += sn9c102_i2c_write(cam, 0x29, 0x30); |
113 | err += sn9c102_i2c_write(cam, 0x2f, 0x3d); | |
114 | err += sn9c102_i2c_write(cam, 0x30, 0x24); | |
115 | err += sn9c102_i2c_write(cam, 0x32, 0x86); | |
116 | err += sn9c102_i2c_write(cam, 0x60, 0xa9); | |
117 | err += sn9c102_i2c_write(cam, 0x61, 0x42); | |
118 | err += sn9c102_i2c_write(cam, 0x65, 0x00); | |
119 | err += sn9c102_i2c_write(cam, 0x69, 0x38); | |
120 | err += sn9c102_i2c_write(cam, 0x6f, 0x88); | |
121 | err += sn9c102_i2c_write(cam, 0x70, 0x0b); | |
122 | err += sn9c102_i2c_write(cam, 0x71, 0x00); | |
123 | err += sn9c102_i2c_write(cam, 0x74, 0x21); | |
124 | err += sn9c102_i2c_write(cam, 0x7d, 0xf7); | |
f327ebbd LR |
125 | break; |
126 | default: | |
127 | break; | |
128 | } | |
b9df978f LR |
129 | |
130 | return err; | |
131 | } | |
132 | ||
133 | ||
f327ebbd LR |
134 | static int ov7630_get_ctrl(struct sn9c102_device* cam, |
135 | struct v4l2_control* ctrl) | |
b9df978f LR |
136 | { |
137 | int err = 0; | |
138 | ||
139 | switch (ctrl->id) { | |
140 | case V4L2_CID_EXPOSURE: | |
f327ebbd LR |
141 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) |
142 | return -EIO; | |
b9df978f LR |
143 | break; |
144 | case V4L2_CID_RED_BALANCE: | |
f327ebbd | 145 | ctrl->value = sn9c102_pread_reg(cam, 0x07); |
b9df978f LR |
146 | break; |
147 | case V4L2_CID_BLUE_BALANCE: | |
f327ebbd LR |
148 | ctrl->value = sn9c102_pread_reg(cam, 0x06); |
149 | break; | |
150 | case SN9C102_V4L2_CID_GREEN_BALANCE: | |
151 | ctrl->value = sn9c102_pread_reg(cam, 0x05); | |
b9df978f LR |
152 | break; |
153 | case V4L2_CID_GAIN: | |
f327ebbd LR |
154 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) |
155 | return -EIO; | |
156 | ctrl->value &= 0x3f; | |
157 | break; | |
158 | case V4L2_CID_DO_WHITE_BALANCE: | |
159 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) | |
160 | return -EIO; | |
161 | ctrl->value &= 0x3f; | |
162 | break; | |
163 | case V4L2_CID_WHITENESS: | |
164 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) | |
165 | return -EIO; | |
166 | ctrl->value &= 0x3f; | |
167 | break; | |
168 | case V4L2_CID_AUTOGAIN: | |
169 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) | |
170 | return -EIO; | |
171 | ctrl->value &= 0x01; | |
172 | break; | |
173 | case V4L2_CID_VFLIP: | |
174 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) | |
175 | return -EIO; | |
176 | ctrl->value = (ctrl->value & 0x80) ? 1 : 0; | |
177 | break; | |
178 | case SN9C102_V4L2_CID_GAMMA: | |
179 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) | |
180 | return -EIO; | |
181 | ctrl->value = (ctrl->value & 0x02) ? 1 : 0; | |
182 | break; | |
183 | case SN9C102_V4L2_CID_BAND_FILTER: | |
184 | if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) | |
185 | return -EIO; | |
186 | ctrl->value = (ctrl->value & 0x02) ? 1 : 0; | |
187 | break; | |
188 | default: | |
189 | return -EINVAL; | |
190 | } | |
191 | ||
192 | return err ? -EIO : 0; | |
193 | } | |
194 | ||
195 | ||
196 | static int ov7630_set_ctrl(struct sn9c102_device* cam, | |
197 | const struct v4l2_control* ctrl) | |
198 | { | |
199 | int err = 0; | |
200 | ||
201 | switch (ctrl->id) { | |
202 | case V4L2_CID_EXPOSURE: | |
203 | err += sn9c102_i2c_write(cam, 0x10, ctrl->value); | |
b9df978f | 204 | break; |
f327ebbd LR |
205 | case V4L2_CID_RED_BALANCE: |
206 | err += sn9c102_write_reg(cam, ctrl->value, 0x07); | |
b9df978f | 207 | break; |
f327ebbd LR |
208 | case V4L2_CID_BLUE_BALANCE: |
209 | err += sn9c102_write_reg(cam, ctrl->value, 0x06); | |
b9df978f | 210 | break; |
f327ebbd LR |
211 | case SN9C102_V4L2_CID_GREEN_BALANCE: |
212 | err += sn9c102_write_reg(cam, ctrl->value, 0x05); | |
b9df978f | 213 | break; |
f327ebbd LR |
214 | case V4L2_CID_GAIN: |
215 | err += sn9c102_i2c_write(cam, 0x00, ctrl->value); | |
b9df978f LR |
216 | break; |
217 | case V4L2_CID_DO_WHITE_BALANCE: | |
218 | err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); | |
219 | break; | |
220 | case V4L2_CID_WHITENESS: | |
221 | err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); | |
222 | break; | |
b9df978f | 223 | case V4L2_CID_AUTOGAIN: |
f327ebbd LR |
224 | err += sn9c102_i2c_write(cam, 0x13, ctrl->value | |
225 | (ctrl->value << 1)); | |
b9df978f LR |
226 | break; |
227 | case V4L2_CID_VFLIP: | |
228 | err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); | |
229 | break; | |
b9df978f | 230 | case SN9C102_V4L2_CID_GAMMA: |
f327ebbd | 231 | err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); |
b9df978f LR |
232 | break; |
233 | case SN9C102_V4L2_CID_BAND_FILTER: | |
234 | err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); | |
235 | break; | |
236 | default: | |
237 | return -EINVAL; | |
238 | } | |
239 | ||
240 | return err ? -EIO : 0; | |
241 | } | |
242 | ||
243 | ||
244 | static int ov7630_set_crop(struct sn9c102_device* cam, | |
d56410e0 | 245 | const struct v4l2_rect* rect) |
b9df978f | 246 | { |
f327ebbd | 247 | struct sn9c102_sensor* s = sn9c102_get_sensor(cam); |
b9df978f | 248 | int err = 0; |
f327ebbd LR |
249 | u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, |
250 | v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; | |
b9df978f | 251 | |
f327ebbd | 252 | err += sn9c102_write_reg(cam, h_start, 0x12); |
b9df978f LR |
253 | err += sn9c102_write_reg(cam, v_start, 0x13); |
254 | ||
255 | return err; | |
256 | } | |
257 | ||
258 | ||
259 | static int ov7630_set_pix_format(struct sn9c102_device* cam, | |
d56410e0 | 260 | const struct v4l2_pix_format* pix) |
b9df978f LR |
261 | { |
262 | int err = 0; | |
263 | ||
264 | if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) | |
265 | err += sn9c102_write_reg(cam, 0x20, 0x19); | |
266 | else | |
267 | err += sn9c102_write_reg(cam, 0x50, 0x19); | |
268 | ||
269 | return err; | |
270 | } | |
271 | ||
272 | ||
273 | static struct sn9c102_sensor ov7630 = { | |
274 | .name = "OV7630", | |
275 | .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", | |
f327ebbd LR |
276 | .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, |
277 | .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, | |
b9df978f LR |
278 | .frequency = SN9C102_I2C_100KHZ, |
279 | .interface = SN9C102_I2C_2WIRES, | |
280 | .i2c_slave_id = 0x21, | |
281 | .init = &ov7630_init, | |
282 | .qctrl = { | |
283 | { | |
284 | .id = V4L2_CID_GAIN, | |
285 | .type = V4L2_CTRL_TYPE_INTEGER, | |
286 | .name = "global gain", | |
287 | .minimum = 0x00, | |
288 | .maximum = 0x3f, | |
289 | .step = 0x01, | |
290 | .default_value = 0x14, | |
291 | .flags = 0, | |
292 | }, | |
b9df978f LR |
293 | { |
294 | .id = V4L2_CID_EXPOSURE, | |
295 | .type = V4L2_CTRL_TYPE_INTEGER, | |
296 | .name = "exposure", | |
b9df978f LR |
297 | .minimum = 0x00, |
298 | .maximum = 0xff, | |
299 | .step = 0x01, | |
f327ebbd | 300 | .default_value = 0x60, |
b9df978f LR |
301 | .flags = 0, |
302 | }, | |
303 | { | |
f327ebbd | 304 | .id = V4L2_CID_WHITENESS, |
b9df978f | 305 | .type = V4L2_CTRL_TYPE_INTEGER, |
f327ebbd | 306 | .name = "white balance background: red", |
b9df978f | 307 | .minimum = 0x00, |
f327ebbd | 308 | .maximum = 0x3f, |
b9df978f | 309 | .step = 0x01, |
f327ebbd | 310 | .default_value = 0x20, |
b9df978f LR |
311 | .flags = 0, |
312 | }, | |
313 | { | |
314 | .id = V4L2_CID_DO_WHITE_BALANCE, | |
315 | .type = V4L2_CTRL_TYPE_INTEGER, | |
316 | .name = "white balance background: blue", | |
317 | .minimum = 0x00, | |
318 | .maximum = 0x3f, | |
319 | .step = 0x01, | |
320 | .default_value = 0x20, | |
321 | .flags = 0, | |
322 | }, | |
323 | { | |
f327ebbd | 324 | .id = V4L2_CID_RED_BALANCE, |
b9df978f | 325 | .type = V4L2_CTRL_TYPE_INTEGER, |
f327ebbd | 326 | .name = "red balance", |
b9df978f | 327 | .minimum = 0x00, |
f327ebbd | 328 | .maximum = 0x7f, |
b9df978f LR |
329 | .step = 0x01, |
330 | .default_value = 0x20, | |
331 | .flags = 0, | |
332 | }, | |
333 | { | |
f327ebbd LR |
334 | .id = V4L2_CID_BLUE_BALANCE, |
335 | .type = V4L2_CTRL_TYPE_INTEGER, | |
336 | .name = "blue balance", | |
b9df978f | 337 | .minimum = 0x00, |
f327ebbd | 338 | .maximum = 0x7f, |
b9df978f | 339 | .step = 0x01, |
f327ebbd | 340 | .default_value = 0x20, |
b9df978f LR |
341 | .flags = 0, |
342 | }, | |
343 | { | |
344 | .id = V4L2_CID_AUTOGAIN, | |
f327ebbd LR |
345 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
346 | .name = "auto adjust", | |
b9df978f | 347 | .minimum = 0x00, |
f327ebbd | 348 | .maximum = 0x01, |
b9df978f LR |
349 | .step = 0x01, |
350 | .default_value = 0x00, | |
351 | .flags = 0, | |
352 | }, | |
353 | { | |
354 | .id = V4L2_CID_VFLIP, | |
355 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
356 | .name = "vertical flip", | |
357 | .minimum = 0x00, | |
358 | .maximum = 0x01, | |
359 | .step = 0x01, | |
360 | .default_value = 0x01, | |
361 | .flags = 0, | |
362 | }, | |
363 | { | |
f327ebbd | 364 | .id = SN9C102_V4L2_CID_GREEN_BALANCE, |
b9df978f | 365 | .type = V4L2_CTRL_TYPE_INTEGER, |
f327ebbd LR |
366 | .name = "green balance", |
367 | .minimum = 0x00, | |
368 | .maximum = 0x7f, | |
b9df978f | 369 | .step = 0x01, |
f327ebbd | 370 | .default_value = 0x20, |
b9df978f LR |
371 | .flags = 0, |
372 | }, | |
373 | { | |
374 | .id = SN9C102_V4L2_CID_BAND_FILTER, | |
375 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
376 | .name = "band filter", | |
377 | .minimum = 0x00, | |
378 | .maximum = 0x01, | |
379 | .step = 0x01, | |
380 | .default_value = 0x00, | |
381 | .flags = 0, | |
382 | }, | |
383 | { | |
384 | .id = SN9C102_V4L2_CID_GAMMA, | |
385 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
386 | .name = "rgb gamma", | |
387 | .minimum = 0x00, | |
388 | .maximum = 0x01, | |
389 | .step = 0x01, | |
390 | .default_value = 0x00, | |
391 | .flags = 0, | |
392 | }, | |
393 | }, | |
f327ebbd | 394 | .get_ctrl = &ov7630_get_ctrl, |
b9df978f LR |
395 | .set_ctrl = &ov7630_set_ctrl, |
396 | .cropcap = { | |
397 | .bounds = { | |
398 | .left = 0, | |
399 | .top = 0, | |
400 | .width = 640, | |
401 | .height = 480, | |
402 | }, | |
403 | .defrect = { | |
404 | .left = 0, | |
405 | .top = 0, | |
406 | .width = 640, | |
407 | .height = 480, | |
408 | }, | |
409 | }, | |
410 | .set_crop = &ov7630_set_crop, | |
411 | .pix_format = { | |
412 | .width = 640, | |
413 | .height = 480, | |
f327ebbd | 414 | .pixelformat = V4L2_PIX_FMT_SN9C10X, |
b9df978f LR |
415 | .priv = 8, |
416 | }, | |
417 | .set_pix_format = &ov7630_set_pix_format | |
418 | }; | |
419 | ||
420 | ||
421 | int sn9c102_probe_ov7630(struct sn9c102_device* cam) | |
422 | { | |
f327ebbd | 423 | int pid, ver, err = 0; |
b9df978f | 424 | |
f327ebbd LR |
425 | switch (sn9c102_get_bridge(cam)) { |
426 | case BRIDGE_SN9C101: | |
427 | case BRIDGE_SN9C102: | |
f423b9a8 LR |
428 | err += sn9c102_write_reg(cam, 0x01, 0x01); |
429 | err += sn9c102_write_reg(cam, 0x00, 0x01); | |
430 | err += sn9c102_write_reg(cam, 0x28, 0x17); | |
f327ebbd LR |
431 | break; |
432 | case BRIDGE_SN9C103: /* do _not_ change anything! */ | |
433 | err += sn9c102_write_reg(cam, 0x09, 0x01); | |
434 | err += sn9c102_write_reg(cam, 0x42, 0x01); | |
435 | err += sn9c102_write_reg(cam, 0x28, 0x17); | |
436 | err += sn9c102_write_reg(cam, 0x44, 0x02); | |
437 | pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); | |
438 | if (err || pid < 0) { /* try a different initialization */ | |
439 | err = sn9c102_write_reg(cam, 0x01, 0x01); | |
440 | err += sn9c102_write_reg(cam, 0x00, 0x01); | |
441 | } | |
442 | break; | |
443 | default: | |
444 | break; | |
445 | } | |
b9df978f | 446 | |
f327ebbd LR |
447 | pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); |
448 | ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); | |
449 | if (err || pid < 0 || ver < 0) | |
450 | return -EIO; | |
451 | if (pid != 0x76 || ver != 0x31) | |
b9df978f | 452 | return -ENODEV; |
2ffab02f LR |
453 | sn9c102_attach_sensor(cam, &ov7630); |
454 | ||
b9df978f LR |
455 | return 0; |
456 | } |