diff options
Diffstat (limited to 'drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c')
-rw-r--r-- | drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c new file mode 100644 index 00000000000..b8156855f2b --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c | |||
@@ -0,0 +1,611 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | ||
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | ||
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | ||
5 | * Copyright (c) 2008 Erik Andrén | ||
6 | * Copyright (c) 2008 Chia-I Wu | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | ||
23 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | ||
24 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | ||
25 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | ||
26 | * P/N 861075-0040: Sensor HDCS1000 ASIC | ||
27 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | ||
28 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | ||
29 | */ | ||
30 | |||
31 | #include "stv06xx_hdcs.h" | ||
32 | |||
33 | static const struct ctrl hdcs1x00_ctrl[] = { | ||
34 | { | ||
35 | { | ||
36 | .id = V4L2_CID_EXPOSURE, | ||
37 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
38 | .name = "exposure", | ||
39 | .minimum = 0x00, | ||
40 | .maximum = 0xff, | ||
41 | .step = 0x1, | ||
42 | .default_value = HDCS_DEFAULT_EXPOSURE, | ||
43 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
44 | }, | ||
45 | .set = hdcs_set_exposure, | ||
46 | .get = hdcs_get_exposure | ||
47 | }, { | ||
48 | { | ||
49 | .id = V4L2_CID_GAIN, | ||
50 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
51 | .name = "gain", | ||
52 | .minimum = 0x00, | ||
53 | .maximum = 0xff, | ||
54 | .step = 0x1, | ||
55 | .default_value = HDCS_DEFAULT_GAIN, | ||
56 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
57 | }, | ||
58 | .set = hdcs_set_gain, | ||
59 | .get = hdcs_get_gain | ||
60 | } | ||
61 | }; | ||
62 | |||
63 | static struct v4l2_pix_format hdcs1x00_mode[] = { | ||
64 | { | ||
65 | HDCS_1X00_DEF_WIDTH, | ||
66 | HDCS_1X00_DEF_HEIGHT, | ||
67 | V4L2_PIX_FMT_SGRBG8, | ||
68 | V4L2_FIELD_NONE, | ||
69 | .sizeimage = | ||
70 | HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, | ||
71 | .bytesperline = HDCS_1X00_DEF_WIDTH, | ||
72 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
73 | .priv = 1 | ||
74 | } | ||
75 | }; | ||
76 | |||
77 | static const struct ctrl hdcs1020_ctrl[] = { | ||
78 | { | ||
79 | { | ||
80 | .id = V4L2_CID_EXPOSURE, | ||
81 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
82 | .name = "exposure", | ||
83 | .minimum = 0x00, | ||
84 | .maximum = 0xffff, | ||
85 | .step = 0x1, | ||
86 | .default_value = HDCS_DEFAULT_EXPOSURE, | ||
87 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
88 | }, | ||
89 | .set = hdcs_set_exposure, | ||
90 | .get = hdcs_get_exposure | ||
91 | }, { | ||
92 | { | ||
93 | .id = V4L2_CID_GAIN, | ||
94 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
95 | .name = "gain", | ||
96 | .minimum = 0x00, | ||
97 | .maximum = 0xff, | ||
98 | .step = 0x1, | ||
99 | .default_value = HDCS_DEFAULT_GAIN, | ||
100 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
101 | }, | ||
102 | .set = hdcs_set_gain, | ||
103 | .get = hdcs_get_gain | ||
104 | } | ||
105 | }; | ||
106 | |||
107 | static struct v4l2_pix_format hdcs1020_mode[] = { | ||
108 | { | ||
109 | HDCS_1020_DEF_WIDTH, | ||
110 | HDCS_1020_DEF_HEIGHT, | ||
111 | V4L2_PIX_FMT_SGRBG8, | ||
112 | V4L2_FIELD_NONE, | ||
113 | .sizeimage = | ||
114 | HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, | ||
115 | .bytesperline = HDCS_1020_DEF_WIDTH, | ||
116 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
117 | .priv = 1 | ||
118 | } | ||
119 | }; | ||
120 | |||
121 | enum hdcs_power_state { | ||
122 | HDCS_STATE_SLEEP, | ||
123 | HDCS_STATE_IDLE, | ||
124 | HDCS_STATE_RUN | ||
125 | }; | ||
126 | |||
127 | /* no lock? */ | ||
128 | struct hdcs { | ||
129 | enum hdcs_power_state state; | ||
130 | int w, h; | ||
131 | |||
132 | /* visible area of the sensor array */ | ||
133 | struct { | ||
134 | int left, top; | ||
135 | int width, height; | ||
136 | int border; | ||
137 | } array; | ||
138 | |||
139 | struct { | ||
140 | /* Column timing overhead */ | ||
141 | u8 cto; | ||
142 | /* Column processing overhead */ | ||
143 | u8 cpo; | ||
144 | /* Row sample period constant */ | ||
145 | u16 rs; | ||
146 | /* Exposure reset duration */ | ||
147 | u16 er; | ||
148 | } exp; | ||
149 | |||
150 | int psmp; | ||
151 | u8 exp_cache, gain_cache; | ||
152 | }; | ||
153 | |||
154 | static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) | ||
155 | { | ||
156 | u8 regs[I2C_MAX_BYTES * 2]; | ||
157 | int i; | ||
158 | |||
159 | if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) || | ||
160 | (reg + len > 0xff))) | ||
161 | return -EINVAL; | ||
162 | |||
163 | for (i = 0; i < len; i++) { | ||
164 | regs[2 * i] = reg; | ||
165 | regs[2 * i + 1] = vals[i]; | ||
166 | /* All addresses are shifted left one bit | ||
167 | * as bit 0 toggles r/w */ | ||
168 | reg += 2; | ||
169 | } | ||
170 | |||
171 | return stv06xx_write_sensor_bytes(sd, regs, len); | ||
172 | } | ||
173 | |||
174 | static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state) | ||
175 | { | ||
176 | struct hdcs *hdcs = sd->sensor_priv; | ||
177 | u8 val; | ||
178 | int ret; | ||
179 | |||
180 | if (hdcs->state == state) | ||
181 | return 0; | ||
182 | |||
183 | /* we need to go idle before running or sleeping */ | ||
184 | if (hdcs->state != HDCS_STATE_IDLE) { | ||
185 | ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); | ||
186 | if (ret) | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | hdcs->state = HDCS_STATE_IDLE; | ||
191 | |||
192 | if (state == HDCS_STATE_IDLE) | ||
193 | return 0; | ||
194 | |||
195 | switch (state) { | ||
196 | case HDCS_STATE_SLEEP: | ||
197 | val = HDCS_SLEEP_MODE; | ||
198 | break; | ||
199 | |||
200 | case HDCS_STATE_RUN: | ||
201 | val = HDCS_RUN_ENABLE; | ||
202 | break; | ||
203 | |||
204 | default: | ||
205 | return -EINVAL; | ||
206 | } | ||
207 | |||
208 | ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val); | ||
209 | |||
210 | /* Update the state if the write succeeded */ | ||
211 | if (!ret) | ||
212 | hdcs->state = state; | ||
213 | |||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | static int hdcs_reset(struct sd *sd) | ||
218 | { | ||
219 | struct hdcs *hdcs = sd->sensor_priv; | ||
220 | int err; | ||
221 | |||
222 | err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1); | ||
223 | if (err < 0) | ||
224 | return err; | ||
225 | |||
226 | err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0); | ||
227 | if (err < 0) | ||
228 | hdcs->state = HDCS_STATE_IDLE; | ||
229 | |||
230 | return err; | ||
231 | } | ||
232 | |||
233 | static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
234 | { | ||
235 | struct sd *sd = (struct sd *) gspca_dev; | ||
236 | struct hdcs *hdcs = sd->sensor_priv; | ||
237 | |||
238 | *val = hdcs->exp_cache; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | ||
244 | { | ||
245 | struct sd *sd = (struct sd *) gspca_dev; | ||
246 | struct hdcs *hdcs = sd->sensor_priv; | ||
247 | int rowexp, srowexp; | ||
248 | int max_srowexp; | ||
249 | /* Column time period */ | ||
250 | int ct; | ||
251 | /* Column processing period */ | ||
252 | int cp; | ||
253 | /* Row processing period */ | ||
254 | int rp; | ||
255 | /* Minimum number of column timing periods | ||
256 | within the column processing period */ | ||
257 | int mnct; | ||
258 | int cycles, err; | ||
259 | u8 exp[14]; | ||
260 | |||
261 | val &= 0xff; | ||
262 | hdcs->exp_cache = val; | ||
263 | |||
264 | cycles = val * HDCS_CLK_FREQ_MHZ * 257; | ||
265 | |||
266 | ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); | ||
267 | cp = hdcs->exp.cto + (hdcs->w * ct / 2); | ||
268 | |||
269 | /* the cycles one row takes */ | ||
270 | rp = hdcs->exp.rs + cp; | ||
271 | |||
272 | rowexp = cycles / rp; | ||
273 | |||
274 | /* the remaining cycles */ | ||
275 | cycles -= rowexp * rp; | ||
276 | |||
277 | /* calculate sub-row exposure */ | ||
278 | if (IS_1020(sd)) { | ||
279 | /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */ | ||
280 | srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct; | ||
281 | |||
282 | mnct = (hdcs->exp.er + 12 + ct - 1) / ct; | ||
283 | max_srowexp = hdcs->w - mnct; | ||
284 | } else { | ||
285 | /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */ | ||
286 | srowexp = cp - hdcs->exp.er - 6 - cycles; | ||
287 | |||
288 | mnct = (hdcs->exp.er + 5 + ct - 1) / ct; | ||
289 | max_srowexp = cp - mnct * ct - 1; | ||
290 | } | ||
291 | |||
292 | if (srowexp < 0) | ||
293 | srowexp = 0; | ||
294 | else if (srowexp > max_srowexp) | ||
295 | srowexp = max_srowexp; | ||
296 | |||
297 | if (IS_1020(sd)) { | ||
298 | exp[0] = HDCS20_CONTROL; | ||
299 | exp[1] = 0x00; /* Stop streaming */ | ||
300 | exp[2] = HDCS_ROWEXPL; | ||
301 | exp[3] = rowexp & 0xff; | ||
302 | exp[4] = HDCS_ROWEXPH; | ||
303 | exp[5] = rowexp >> 8; | ||
304 | exp[6] = HDCS20_SROWEXP; | ||
305 | exp[7] = (srowexp >> 2) & 0xff; | ||
306 | exp[8] = HDCS20_ERROR; | ||
307 | exp[9] = 0x10; /* Clear exposure error flag*/ | ||
308 | exp[10] = HDCS20_CONTROL; | ||
309 | exp[11] = 0x04; /* Restart streaming */ | ||
310 | err = stv06xx_write_sensor_bytes(sd, exp, 6); | ||
311 | } else { | ||
312 | exp[0] = HDCS00_CONTROL; | ||
313 | exp[1] = 0x00; /* Stop streaming */ | ||
314 | exp[2] = HDCS_ROWEXPL; | ||
315 | exp[3] = rowexp & 0xff; | ||
316 | exp[4] = HDCS_ROWEXPH; | ||
317 | exp[5] = rowexp >> 8; | ||
318 | exp[6] = HDCS00_SROWEXPL; | ||
319 | exp[7] = srowexp & 0xff; | ||
320 | exp[8] = HDCS00_SROWEXPH; | ||
321 | exp[9] = srowexp >> 8; | ||
322 | exp[10] = HDCS_STATUS; | ||
323 | exp[11] = 0x10; /* Clear exposure error flag*/ | ||
324 | exp[12] = HDCS00_CONTROL; | ||
325 | exp[13] = 0x04; /* Restart streaming */ | ||
326 | err = stv06xx_write_sensor_bytes(sd, exp, 7); | ||
327 | if (err < 0) | ||
328 | return err; | ||
329 | } | ||
330 | PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d", | ||
331 | val, rowexp, srowexp); | ||
332 | return err; | ||
333 | } | ||
334 | |||
335 | static int hdcs_set_gains(struct sd *sd, u8 g) | ||
336 | { | ||
337 | struct hdcs *hdcs = sd->sensor_priv; | ||
338 | int err; | ||
339 | u8 gains[4]; | ||
340 | |||
341 | hdcs->gain_cache = g; | ||
342 | |||
343 | /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ | ||
344 | if (g > 127) | ||
345 | g = 0x80 | (g / 2); | ||
346 | |||
347 | gains[0] = g; | ||
348 | gains[1] = g; | ||
349 | gains[2] = g; | ||
350 | gains[3] = g; | ||
351 | |||
352 | err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); | ||
353 | return err; | ||
354 | } | ||
355 | |||
356 | static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
357 | { | ||
358 | struct sd *sd = (struct sd *) gspca_dev; | ||
359 | struct hdcs *hdcs = sd->sensor_priv; | ||
360 | |||
361 | *val = hdcs->gain_cache; | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
367 | { | ||
368 | PDEBUG(D_V4L2, "Writing gain %d", val); | ||
369 | return hdcs_set_gains((struct sd *) gspca_dev, | ||
370 | val & 0xff); | ||
371 | } | ||
372 | |||
373 | static int hdcs_set_size(struct sd *sd, | ||
374 | unsigned int width, unsigned int height) | ||
375 | { | ||
376 | struct hdcs *hdcs = sd->sensor_priv; | ||
377 | u8 win[4]; | ||
378 | unsigned int x, y; | ||
379 | int err; | ||
380 | |||
381 | /* must be multiple of 4 */ | ||
382 | width = (width + 3) & ~0x3; | ||
383 | height = (height + 3) & ~0x3; | ||
384 | |||
385 | if (width > hdcs->array.width) | ||
386 | width = hdcs->array.width; | ||
387 | |||
388 | if (IS_1020(sd)) { | ||
389 | /* the borders are also invalid */ | ||
390 | if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP | ||
391 | > hdcs->array.height) | ||
392 | height = hdcs->array.height - 2 * hdcs->array.border - | ||
393 | HDCS_1020_BOTTOM_Y_SKIP; | ||
394 | |||
395 | y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2 | ||
396 | + hdcs->array.top; | ||
397 | } else { | ||
398 | if (height > hdcs->array.height) | ||
399 | height = hdcs->array.height; | ||
400 | |||
401 | y = hdcs->array.top + (hdcs->array.height - height) / 2; | ||
402 | } | ||
403 | |||
404 | x = hdcs->array.left + (hdcs->array.width - width) / 2; | ||
405 | |||
406 | win[0] = y / 4; | ||
407 | win[1] = x / 4; | ||
408 | win[2] = (y + height) / 4 - 1; | ||
409 | win[3] = (x + width) / 4 - 1; | ||
410 | |||
411 | err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4); | ||
412 | if (err < 0) | ||
413 | return err; | ||
414 | |||
415 | /* Update the current width and height */ | ||
416 | hdcs->w = width; | ||
417 | hdcs->h = height; | ||
418 | return err; | ||
419 | } | ||
420 | |||
421 | static int hdcs_probe_1x00(struct sd *sd) | ||
422 | { | ||
423 | struct hdcs *hdcs; | ||
424 | u16 sensor; | ||
425 | int ret; | ||
426 | |||
427 | ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); | ||
428 | if (ret < 0 || sensor != 0x08) | ||
429 | return -ENODEV; | ||
430 | |||
431 | info("HDCS-1000/1100 sensor detected"); | ||
432 | |||
433 | sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; | ||
434 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); | ||
435 | sd->desc.ctrls = hdcs1x00_ctrl; | ||
436 | sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl); | ||
437 | |||
438 | hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); | ||
439 | if (!hdcs) | ||
440 | return -ENOMEM; | ||
441 | |||
442 | hdcs->array.left = 8; | ||
443 | hdcs->array.top = 8; | ||
444 | hdcs->array.width = HDCS_1X00_DEF_WIDTH; | ||
445 | hdcs->array.height = HDCS_1X00_DEF_HEIGHT; | ||
446 | hdcs->array.border = 4; | ||
447 | |||
448 | hdcs->exp.cto = 4; | ||
449 | hdcs->exp.cpo = 2; | ||
450 | hdcs->exp.rs = 186; | ||
451 | hdcs->exp.er = 100; | ||
452 | |||
453 | /* | ||
454 | * Frame rate on HDCS-1000 with STV600 depends on PSMP: | ||
455 | * 4 = doesn't work at all | ||
456 | * 5 = 7.8 fps, | ||
457 | * 6 = 6.9 fps, | ||
458 | * 8 = 6.3 fps, | ||
459 | * 10 = 5.5 fps, | ||
460 | * 15 = 4.4 fps, | ||
461 | * 31 = 2.8 fps | ||
462 | * | ||
463 | * Frame rate on HDCS-1000 with STV602 depends on PSMP: | ||
464 | * 15 = doesn't work at all | ||
465 | * 18 = doesn't work at all | ||
466 | * 19 = 7.3 fps | ||
467 | * 20 = 7.4 fps | ||
468 | * 21 = 7.4 fps | ||
469 | * 22 = 7.4 fps | ||
470 | * 24 = 6.3 fps | ||
471 | * 30 = 5.4 fps | ||
472 | */ | ||
473 | hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; | ||
474 | |||
475 | sd->sensor_priv = hdcs; | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static int hdcs_probe_1020(struct sd *sd) | ||
481 | { | ||
482 | struct hdcs *hdcs; | ||
483 | u16 sensor; | ||
484 | int ret; | ||
485 | |||
486 | ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor); | ||
487 | if (ret < 0 || sensor != 0x10) | ||
488 | return -ENODEV; | ||
489 | |||
490 | info("HDCS-1020 sensor detected"); | ||
491 | |||
492 | sd->gspca_dev.cam.cam_mode = hdcs1020_mode; | ||
493 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); | ||
494 | sd->desc.ctrls = hdcs1020_ctrl; | ||
495 | sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl); | ||
496 | |||
497 | hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); | ||
498 | if (!hdcs) | ||
499 | return -ENOMEM; | ||
500 | |||
501 | /* | ||
502 | * From Andrey's test image: looks like HDCS-1020 upper-left | ||
503 | * visible pixel is at 24,8 (y maybe even smaller?) and lower-right | ||
504 | * visible pixel at 375,299 (x maybe even larger?) | ||
505 | */ | ||
506 | hdcs->array.left = 24; | ||
507 | hdcs->array.top = 4; | ||
508 | hdcs->array.width = HDCS_1020_DEF_WIDTH; | ||
509 | hdcs->array.height = 304; | ||
510 | hdcs->array.border = 4; | ||
511 | |||
512 | hdcs->psmp = 6; | ||
513 | |||
514 | hdcs->exp.cto = 3; | ||
515 | hdcs->exp.cpo = 3; | ||
516 | hdcs->exp.rs = 155; | ||
517 | hdcs->exp.er = 96; | ||
518 | |||
519 | sd->sensor_priv = hdcs; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int hdcs_start(struct sd *sd) | ||
525 | { | ||
526 | PDEBUG(D_STREAM, "Starting stream"); | ||
527 | |||
528 | return hdcs_set_state(sd, HDCS_STATE_RUN); | ||
529 | } | ||
530 | |||
531 | static int hdcs_stop(struct sd *sd) | ||
532 | { | ||
533 | PDEBUG(D_STREAM, "Halting stream"); | ||
534 | |||
535 | return hdcs_set_state(sd, HDCS_STATE_SLEEP); | ||
536 | } | ||
537 | |||
538 | static void hdcs_disconnect(struct sd *sd) | ||
539 | { | ||
540 | PDEBUG(D_PROBE, "Disconnecting the sensor"); | ||
541 | kfree(sd->sensor_priv); | ||
542 | } | ||
543 | |||
544 | static int hdcs_init(struct sd *sd) | ||
545 | { | ||
546 | struct hdcs *hdcs = sd->sensor_priv; | ||
547 | int i, err = 0; | ||
548 | |||
549 | /* Set the STV0602AA in STV0600 emulation mode */ | ||
550 | if (sd->bridge == BRIDGE_STV602) | ||
551 | stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); | ||
552 | |||
553 | /* Execute the bridge init */ | ||
554 | for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) { | ||
555 | err = stv06xx_write_bridge(sd, stv_bridge_init[i][0], | ||
556 | stv_bridge_init[i][1]); | ||
557 | } | ||
558 | if (err < 0) | ||
559 | return err; | ||
560 | |||
561 | /* sensor soft reset */ | ||
562 | hdcs_reset(sd); | ||
563 | |||
564 | /* Execute the sensor init */ | ||
565 | for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) { | ||
566 | err = stv06xx_write_sensor(sd, stv_sensor_init[i][0], | ||
567 | stv_sensor_init[i][1]); | ||
568 | } | ||
569 | if (err < 0) | ||
570 | return err; | ||
571 | |||
572 | /* Enable continuous frame capture, bit 2: stop when frame complete */ | ||
573 | err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3)); | ||
574 | if (err < 0) | ||
575 | return err; | ||
576 | |||
577 | /* Set PGA sample duration | ||
578 | (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ | ||
579 | if (IS_1020(sd)) | ||
580 | err = stv06xx_write_sensor(sd, HDCS_TCTRL, | ||
581 | (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); | ||
582 | else | ||
583 | err = stv06xx_write_sensor(sd, HDCS_TCTRL, | ||
584 | (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp); | ||
585 | if (err < 0) | ||
586 | return err; | ||
587 | |||
588 | err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN); | ||
589 | if (err < 0) | ||
590 | return err; | ||
591 | |||
592 | err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); | ||
593 | if (err < 0) | ||
594 | return err; | ||
595 | |||
596 | err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); | ||
597 | return err; | ||
598 | } | ||
599 | |||
600 | static int hdcs_dump(struct sd *sd) | ||
601 | { | ||
602 | u16 reg, val; | ||
603 | |||
604 | info("Dumping sensor registers:"); | ||
605 | |||
606 | for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { | ||
607 | stv06xx_read_sensor(sd, reg, &val); | ||
608 | info("reg 0x%02x = 0x%02x", reg, val); | ||
609 | } | ||
610 | return 0; | ||
611 | } | ||