Rework sensor detection.
[microdia.git] / sn9c20x-bridge.c
bloba843553edcec4d4bcb92ec75d343d608693dcc16
1 /**
2 * @file sn9c20x.c
3 * @author Dave Neuer
4 * @date 2008-03-02
6 * @brief Common functions and data for the Sonix SN9C20x webcam bridge chips.
8 * @note Copyright (C) Dave Neuer
10 * @par Licences
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/delay.h>
28 #include <linux/errno.h>
29 #include <linux/string.h>
30 #include "sn9c20x.h"
31 #include "sn9c20x-bridge.h"
33 int sn9c20x_set_camera_control(struct usb_sn9c20x *dev,
34 __u32 control, __u32 value)
36 int ret = -EINVAL;
37 switch (control) {
38 case V4L2_CID_CONTRAST:
39 if (dev->camera.set_contrast) {
40 dev->vsettings.contrast = value;
41 ret = dev->camera.set_contrast(dev);
43 break;
44 case V4L2_CID_BRIGHTNESS:
45 if (dev->camera.set_brightness) {
46 dev->vsettings.brightness = value;
47 ret = dev->camera.set_brightness(dev);
49 break;
50 case V4L2_CID_GAMMA:
51 if (dev->camera.set_gamma) {
52 dev->vsettings.gamma = value;
53 ret = dev->camera.set_gamma(dev);
55 break;
56 case V4L2_CID_SHARPNESS:
57 if (dev->camera.set_sharpness) {
58 dev->vsettings.sharpness = value;
59 ret = dev->camera.set_sharpness(dev);
61 break;
62 case V4L2_CID_RED_BALANCE:
63 if (dev->camera.set_red_gain) {
64 dev->vsettings.red_gain = value & 0x7f;
65 ret = dev->camera.set_red_gain(dev);
67 break;
68 case V4L2_CID_BLUE_BALANCE:
69 if (dev->camera.set_blue_gain) {
70 dev->vsettings.blue_gain = value & 0x7f;
71 ret = dev->camera.set_blue_gain(dev);
73 break;
74 case V4L2_CID_HFLIP:
75 if (dev->camera.set_hvflip) {
76 dev->vsettings.hflip = value;
77 ret = dev->camera.set_hvflip(dev);
79 break;
80 case V4L2_CID_VFLIP:
81 if (dev->camera.set_hvflip) {
82 dev->vsettings.vflip = value;
83 ret = dev->camera.set_hvflip(dev);
85 break;
86 case V4L2_CID_AUTOGAIN:
87 if (dev->camera.set_auto_gain) {
88 dev->vsettings.auto_gain = value;
89 ret = dev->camera.set_auto_gain(dev);
91 break;
92 case V4L2_CID_EXPOSURE:
93 if (dev->camera.set_exposure) {
94 dev->vsettings.exposure = value;
95 ret = dev->camera.set_exposure(dev);
97 break;
98 case V4L2_CID_GAIN:
99 if (dev->camera.set_gain) {
100 dev->vsettings.gain = value;
101 ret = dev->camera.set_gain(dev);
103 break;
104 case V4L2_CID_AUTO_WHITE_BALANCE:
105 if (dev->camera.set_auto_whitebalance) {
106 dev->vsettings.auto_whitebalance = value;
107 ret = dev->camera.set_auto_whitebalance(dev);
109 break;
110 case V4L2_CID_EXPOSURE_AUTO:
111 if (dev->camera.set_auto_exposure) {
112 dev->vsettings.auto_exposure = value;
113 ret = dev->camera.set_auto_exposure(dev);
115 break;
117 return ret;
121 * @brief Switch Video stream on and off
123 * @param dev Pointer to device structure
124 * @param enable On or off
126 * @author Brian Johnson
128 * @return 0 or negative error code
131 int sn9c20x_enable_video(struct usb_sn9c20x *dev, int enable)
133 __u8 value;
134 int ret;
136 ret = usb_sn9c20x_control_read(dev, 0x1061, &value, 1);
137 if (ret < 0)
138 return ret;
140 if (enable) {
141 value |= 0x02;
142 ret = usb_sn9c20x_control_write(dev, 0x1061, &value, 1);
143 } else {
144 value &= ~0x02;
145 ret = usb_sn9c20x_control_write(dev, 0x1061, &value, 1);
148 sn9c20x_set_LEDs(dev, enable);
150 return ret;
154 * @brief Switch LEDs on and off
156 * @param dev Pointer to device structure
157 * @param enable On or off
159 * @author GWater
161 * @return 0 or negative error code
164 int sn9c20x_set_LEDs(struct usb_sn9c20x *dev, int enable)
166 int ret;
167 __u8 led[2];
169 int sensor = dev->camera.sensor;
171 if (enable) {
172 switch (sensor) {
173 case MT9M001_SENSOR:
174 case HV7131R_SENSOR:
175 case SOI968_SENSOR:
176 led[0] = 0x80;
177 led[1] = 0xa0;
178 break;
179 case MT9V011_SENSOR:
180 case OV7660_SENSOR:
181 led[0] = 0x40;
182 led[1] = 0x60;
183 break;
184 case MT9M111_SENSOR:
185 led[0] = 0x00;
186 led[1] = 0x60;
187 break;
188 default:
189 led[0] = 0x00;
190 led[1] = 0x20;
192 } else {
193 switch (sensor) {
194 case HV7131R_SENSOR:
195 case MT9M001_SENSOR:
196 case MT9V011_SENSOR:
197 led[0] = 0xa0;
198 led[1] = 0xa0;
199 break;
200 case MT9M111_SENSOR:
201 case OV7660_SENSOR:
202 led[0] = 0x20;
203 led[1] = 0x60;
204 break;
205 default:
206 led[0] = 0x20;
207 led[1] = 0x20;
211 ret = usb_sn9c20x_control_write(dev, 0x1006, led, 2);
213 return ret;
217 * @brief Initializes Micro-Controller's I2C interface
219 * @author Neekhil
221 * @param dev Pointer to the device
223 * @return Zero (success) or negative (USB-error value)
226 int sn9c20x_i2c_initialize(struct usb_sn9c20x *dev)
228 __u8 buf[9];
229 int ret;
231 dev->camera.i2c_flags = SN9C20X_I2C_2WIRE;
233 buf[0] = dev->camera.i2c_flags;
234 buf[1] = dev->camera.address;
235 buf[2] = 0x00;
236 buf[3] = 0x00;
237 buf[4] = 0x00;
238 buf[5] = 0x00;
239 buf[6] = 0x00;
240 buf[7] = 0x00;
241 buf[8] = 0x03;
242 /* Initialize I2C registers to avoid getting no ACK at first I2C operation: */
243 /* Get green diagonal bands w/o this dummy write, Bridge does not know sensor address ? */
244 ret = usb_sn9c20x_control_write(dev, 0x10c0, buf, 9);
245 if (ret < 0)
246 return ret;
247 else
248 return 0;
252 * @brief Wait until the I2C slave is ready for the next operation
254 * @param dev Pointer to the device
255 * @param highspeed
256 * @param slave_error
258 * @return Zero for success or a negative error value
261 int sn9c20x_i2c_ack_wait(struct usb_sn9c20x *dev, bool highspeed, bool *slave_error)
263 int ret, i;
264 __u8 readbuf;
265 int delay = highspeed ? 100 : 400;
267 for (i = 0; i < 5; i++) {
268 ret = usb_sn9c20x_control_read(dev, 0x10c0, &readbuf, 1);
270 if (ret < 0)
271 return ret;
272 else if (readbuf & SN9C20X_I2C_ERROR) {
273 *slave_error = 1;
274 /* probably should come up w/ an error value and
275 * return it via the error return */
276 return 0;
277 } else if (readbuf & SN9C20X_I2C_READY)
278 return 0;
279 else
280 udelay(delay);
282 return -EBUSY;
286 * @brief Read up to 5 bytes of data from an I2C slave
288 * @param dev Pointer to the device
289 * @param nbytes Number of bytes to read
290 * @param address The address of the register on the slave to read
291 * @param result A pointer to the location at which the result should be stored
293 * @return Zero for success or a negative error value
296 int sn9c20x_read_i2c_data(struct usb_sn9c20x *dev, __u8 nbytes,
297 __u8 address, __u8 *result)
299 int ret, i, j;
300 __u8 row[5];
302 if (!dev || nbytes > 4)
303 return -EINVAL;
305 /* first, we must do a dummy write of just the address */
306 ret = sn9c20x_write_i2c_data(dev, 0, address, NULL);
307 if (ret < 0)
308 return ret;
310 memset(row, 0, 5);
311 /* now we issue the same command but with the read bit set
312 * and no slave register address */
313 dev->camera.i2c_flags |= SN9C20X_I2C_READ;
314 ret = sn9c20x_write_i2c_data(dev, nbytes - 1, 0, row);
315 dev->camera.i2c_flags &= ~SN9C20X_I2C_READ;
316 if (ret < 0)
317 return ret;
319 /* finally, ask the bridge for the data */
320 ret = usb_sn9c20x_control_read(dev, 0x10c2, row, 5);
321 if (ret < 0)
322 return ret;
324 UDIA_DEBUG("I2C read: %02x %02x %02x %02x %02x %02x\n",
325 address, row[0], row[1], row[2], row[3], row[4]);
327 for (i = 0, j = 5 - nbytes; i < nbytes; i++, j++)
328 result[i] = row[j];
330 return 0;
334 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
336 * @param dev Pointer to the device
337 * @param datalen Number of 16bit values to read
338 * @param address The address of the register on the slave to read
339 * @param result A pointer to the location at which the result should be stored
341 * @return Zero for success or a negative error value
344 int sn9c20x_read_i2c_data16(struct usb_sn9c20x *dev, __u8 datalen,
345 __u8 address, __u16 *result)
347 __u8 result8[4];
348 __u8 k;
349 int ret;
351 if (datalen > 2)
352 return -EINVAL;
353 ret = sn9c20x_read_i2c_data(dev, 2*datalen, address, result8);
354 for (k = 0; k < datalen; k++)
355 result[k] = (result8[k*2] << 8) | result8[k*2+1];
356 return ret;
359 static const char *wasread = "read from";
360 static const char *waswrite = "write to";
363 * @brief Write up to 5 bytes of data to an I2C slave
365 * @param dev Pointer to the device
366 * @param nbytes The number of bytes of data
367 * @param address The address of the register on the slave to write
368 * @param data An array containing the data to write
369 * @param last_byte The byte to be sent as last byte of control sequence
371 * @return Zero for success or a negative error value
374 int sn9c20x_write_i2c_data_ext(struct usb_sn9c20x *dev, __u8 nbytes,
375 __u8 address, const __u8 data[nbytes], __u8 last_byte)
377 int ret, i;
378 __u8 row[8];
379 bool slave_error = 0;
381 if (!dev || (nbytes > 0 && !data) || nbytes > 4)
382 return -EINVAL;
384 /* from the point of view of the bridge, the length
385 * includes the address */
386 row[0] = dev->camera.i2c_flags | ((nbytes + 1) << 4);
387 row[1] = dev->camera.address;
388 row[2] = address;
389 row[7] = last_byte;
391 for (i = 0; i < 4; i++)
392 row[i + 3] = i < nbytes ? data[i] : 0;
394 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
395 (dev->camera.i2c_flags & SN9C20X_I2C_READ ? wasread : waswrite),
396 address, row[0], row[1], row[2], row[3], row[4], row[5], row[6],
397 row[7]);
399 ret = usb_sn9c20x_control_write(dev, 0x10c0, row, 8);
400 if (ret >= 0)
401 ret = sn9c20x_i2c_ack_wait(dev,
402 dev->camera.i2c_flags & SN9C20X_I2C_400KHZ,
403 &slave_error);
405 if (slave_error) {
406 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
407 dev->camera.address, (dev->camera.i2c_flags &
408 SN9C20X_I2C_READ ? wasread : waswrite), address);
409 return -1000;
410 /* there should be no interference with USB errors */
413 if (ret < 0) {
414 /* we got no ack */
415 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
416 dev->camera.address, (dev->camera.i2c_flags &
417 SN9C20X_I2C_READ ? wasread : waswrite), address);
418 return ret;
421 return 0;
425 * @brief Write up to 2 16bit values als single bytes to an I2C slave
427 * @param dev Pointer to the device
428 * @param datalen The number of 16bit data values to write
429 * @param address The address of the register on the slave to write
430 * @param data An array containing the data to write
432 * @return Zero for success or a negative error value
435 int sn9c20x_write_i2c_data16(struct usb_sn9c20x *dev, __u8 datalen,
436 __u8 address, const __u16 data[datalen])
438 __u8 data8[4];
439 __u8 k;
440 int ret;
442 if (datalen > 2)
443 return -EINVAL;
444 for (k = 0; k < datalen; k++) {
445 data8[k*2] = data[k] >> 8;
446 data8[k*2+1] = data[k] & 0xff;
448 ret = sn9c20x_write_i2c_data(dev, 2*datalen, address, data8);
449 return ret;
452 int sn9c20x_write_i2c_array(struct usb_sn9c20x *dev,
453 struct sn9c20x_i2c_regs *regs, int bits16)
455 int i;
456 int ret = 0;
457 __u16 value16;
458 __u8 value8;
459 for (i = 0;; i++) {
460 if (regs[i].address == 0xff)
461 break;
462 if (bits16) {
463 value16 = regs[i].value;
464 ret = sn9c20x_write_i2c_data16(dev, 1,
465 regs[i].address, &value16);
466 } else {
467 value8 = (__u8)regs[i].value;
468 ret = sn9c20x_write_i2c_data(dev, 1,
469 regs[i].address, &value8);
471 if (ret < 0)
472 return ret;
474 return ret;
478 * @brief Set contrast inside sn9c20x chip
480 * @author Comer352l
482 * @param dev Pointer to the device
484 * @return Zero (success) or negative (USB-error value)
487 int sn9c20x_set_contrast(struct usb_sn9c20x *dev)
489 /* from 0x26 to 0x4b */
490 __u8 brightness_contrast[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
491 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
492 0xfd, 0x0f, 0x0, 0x0, 0x0};
493 __u8 contrast_val = (dev->vsettings.contrast) * 0x25 / 0x100;
494 __u8 brightness_val = dev->vsettings.brightness;
496 brightness_val -= 0x80;
497 brightness_contrast[18] = brightness_val;
499 contrast_val += 0x26;
500 brightness_contrast[2] = contrast_val;
501 brightness_contrast[0] = 0x13 + (brightness_contrast[2] - 0x26) * 0x13 / 0x25;
502 brightness_contrast[4] = 0x7 + (brightness_contrast[2] - 0x26) * 0x7 / 0x25;
504 return usb_sn9c20x_control_write(dev, 0x10e1, brightness_contrast, 21);
508 * @brief Set brightness inside sn9c20x chip
510 * @author Comer352l
512 * @param dev Pointer to the device
514 * @return Zero (success) or negative (USB-error value)
516 * Wrapper for sn9c20x_set_contrast
519 int sn9c20x_set_brightness(struct usb_sn9c20x *dev)
521 return sn9c20x_set_contrast(dev);
525 * @brief Set gamma inside sn9c20x chip
527 * @author Comer352l
529 * @param dev Pointer to the device
531 * @return Zero (success) or negative (USB-error value)
534 int sn9c20x_set_gamma(struct usb_sn9c20x *dev)
536 int value = (dev->vsettings.gamma) * 0xb8 / 0x100;
537 int r = 0;
539 __u8 gamma_val[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
540 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
542 gamma_val[0] = 0x0a;
543 gamma_val[1] = 0x13 + (value * (0xcb - 0x13) / 0xb8);
544 gamma_val[2] = 0x25 + (value * (0xee - 0x25) / 0xb8);
545 gamma_val[3] = 0x37 + (value * (0xfa - 0x37) / 0xb8);
546 gamma_val[4] = 0x45 + (value * (0xfc - 0x45) / 0xb8);
547 gamma_val[5] = 0x55 + (value * (0xfb - 0x55) / 0xb8);
548 gamma_val[6] = 0x65 + (value * (0xfc - 0x65) / 0xb8);
549 gamma_val[7] = 0x74 + (value * (0xfd - 0x74) / 0xb8);
550 gamma_val[8] = 0x83 + (value * (0xfe - 0x83) / 0xb8);
551 gamma_val[9] = 0x92 + (value * (0xfc - 0x92) / 0xb8);
552 gamma_val[10] = 0xa1 + (value * (0xfc - 0xa1) / 0xb8);
553 gamma_val[11] = 0xb0 + (value * (0xfc - 0xb0) / 0xb8);
554 gamma_val[12] = 0xbf + (value * (0xfb - 0xbf) / 0xb8);
555 gamma_val[13] = 0xce + (value * (0xfb - 0xce) / 0xb8);
556 gamma_val[14] = 0xdf + (value * (0xfd - 0xdf) / 0xb8);
557 gamma_val[15] = 0xea + (value * (0xf9 - 0xea) / 0xb8);
558 gamma_val[16] = 0xf5;
560 r = usb_sn9c20x_control_write(dev, 0x1190, gamma_val, 17);
562 return r;
566 * @brief Set sharpness inside sn9c20x chip
568 * @author Comer352l
570 * @param dev Pointer to the device
572 * @return Zero (success) or negative (USB-error value)
575 int sn9c20x_set_sharpness(struct usb_sn9c20x *dev)
577 __u8 val[1];
578 int ret;
580 ret = usb_sn9c20x_control_read(dev, SN9C20X_SHARPNESS, val, 1);
581 if (ret < 0)
582 return ret;
583 val[0] = (val[0] & 0xc0) | (dev->vsettings.sharpness & 0x3f);
584 ret = usb_sn9c20x_control_write(dev, SN9C20X_SHARPNESS, val, 1);
585 if (ret < 0)
586 return ret;
587 else
588 return 0;
592 * @brief Set red gain inside sn9c20x chip
594 * @author Brian Johnson
596 * @param dev Pointer to the device
598 * @return Zero (success) or negative (USB-error value)
601 int sn9c20x_set_red_gain(struct usb_sn9c20x *dev)
603 __u8 val;
604 int ret;
606 val = dev->vsettings.red_gain;
607 ret = usb_sn9c20x_control_write(dev, SN9C20X_RED_GAIN, &val, 1);
608 if (ret < 0)
609 return ret;
610 else
611 return 0;
615 * @brief Set blue gain inside sn9c20x chip
617 * @author Brian Johnson
619 * @param dev Pointer to the device
621 * @return Zero (success) or negative (USB-error value)
624 int sn9c20x_set_blue_gain(struct usb_sn9c20x *dev)
626 __u8 val;
627 int ret;
629 val = dev->vsettings.blue_gain;
630 ret = usb_sn9c20x_control_write(dev, SN9C20X_BLUE_GAIN, &val, 1);
631 if (ret < 0)
632 return ret;
633 else
634 return 0;
639 * @brief Calculate closest resolution to input from application
641 * @author Brian Johnson
643 * @param dev Pointer to the device structure
644 * @param width Requested width of video stream
645 * @param height Requested height of video stream
647 * @retval width Closest possible width of video stream
648 * @retval height Closest possible height of video stream
650 * @return Number of the
653 int sn9c20x_get_closest_resolution(struct usb_sn9c20x *dev,
654 int *width, int *height)
656 int i;
658 for (i = SN9C20X_N_MODES - 1; i >= 0; i--) {
659 if (*width >= sn9c20x_modes[i].width
660 && *height >= sn9c20x_modes[i].height)
661 break;
664 *width = sn9c20x_modes[i].width;
665 *height = sn9c20x_modes[i].height;
667 return i;
671 * @brief Set resolution inside sn9c20x chip
673 * @author Brian Johnson
675 * @param dev Pointer to the device
676 * @param width Width
677 * @param height Height
679 * @return 0
682 int sn9c20x_set_resolution(struct usb_sn9c20x *dev,
683 int width, int height)
685 int ret;
686 __u8 scale;
687 __u8 window[6];
688 __u8 clrwindow[5];
689 struct sn9c20x_video_mode *mode;
691 ret = sn9c20x_get_closest_resolution(dev, &width, &height);
693 mode = &sn9c20x_modes[ret];
695 dev->vsettings.format.width = mode->width;
696 dev->vsettings.format.height = mode->height;
698 clrwindow[0] = 0;
699 clrwindow[1] = mode->width >> 2;
700 clrwindow[2] = 0;
701 clrwindow[3] = mode->height >> 1;
702 clrwindow[4] = ((mode->width >> 10) & 0x01) |
703 ((mode->height >> 10) & 0x06);
705 scale = mode->scale;
707 window[0] = (mode->window[0] + dev->camera.hstart) & 0xff;
708 window[1] = (mode->window[0] + dev->camera.hstart) >> 8;
709 window[2] = (mode->window[1] + dev->camera.vstart) & 0xff;
710 window[3] = (mode->window[1] + dev->camera.vstart) >> 8;
711 window[4] = mode->window[2] >> 4;
712 window[5] = mode->window[3] >> 3;
714 usb_sn9c20x_control_write(dev, 0x10fb, clrwindow, 5);
715 usb_sn9c20x_control_write(dev, 0x1180, window, 6);
716 usb_sn9c20x_control_write(dev, SN9C20X_SCALE, &scale, 1);
718 UDIA_DEBUG("Set mode [%dx%d]\n", mode->width, mode->height);
720 return 0;
724 int sn9c20x_set_format(struct usb_sn9c20x *dev, __u32 format)
726 int i;
727 int ret = -EINVAL;
728 for (i = 0; i < SN9C20X_N_FMTS; i++) {
729 if (sn9c20x_fmts[i].pix_fmt == format) {
730 dev->vsettings.format.bytesperline =
731 dev->vsettings.format.width *
732 sn9c20x_fmts[i].depth / 8;
733 dev->vsettings.format.sizeimage =
734 dev->vsettings.format.height *
735 dev->vsettings.format.bytesperline;
736 dev->vsettings.format.pixelformat = format;
737 dev->vsettings.format.colorspace = V4L2_COLORSPACE_SRGB;
738 dev->vsettings.format.priv = 0;
739 sn9c20x_fmts[i].set_format(dev);
740 ret = 0;
741 break;
744 return ret;
747 void sn9c20x_set_raw(struct usb_sn9c20x *dev)
749 __u8 value;
750 value = 0x2d;
751 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
754 void sn9c20x_set_jpeg(struct usb_sn9c20x *dev)
756 __u8 value;
757 value = 0x2c;
758 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
761 void sn9c20x_set_yuv420(struct usb_sn9c20x *dev)
763 __u8 value;
764 value = 0x2f;
765 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
768 void sn9c20x_set_yuv422(struct usb_sn9c20x *dev)
770 __u8 value;
771 value = 0x2e;
772 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
776 * @brief This function initializes the SN9C20x bridge,
777 * these are the bare minimum writes that must be done for
778 * the bridge to work correctly.
780 * @author Neekhil
782 * @param dev Pointer to the device
784 * @return Zero for success or error value
787 int sn9c20x_initialize(struct usb_sn9c20x *dev)
789 int ret, i;
791 __u16 reg;
792 __u8 value;
794 __u16 regs[][2] = {
795 {0x1000, 0x78},
796 {0x1001, 0x40},
797 {0x1002, 0x1c},
798 {0x1020, 0x80},
799 {0x1061, 0x01},
800 {0x1067, 0x40},
801 {0x1068, 0x30},
802 {0x1069, 0x20},
803 {0x106a, 0x10},
804 {0x106b, 0x08},
805 {0x1188, 0x87},
806 {0x11a1, 0x00},
807 {0x11a2, 0x00},
808 {0x11a3, 0x6a},
809 {0x11a4, 0x50},
810 {0x11ab, 0x00},
811 {0x11ac, 0x00},
812 {0x11ad, 0x50},
813 {0x11ae, 0x3c},
814 {0x118a, 0x04},
815 {0x0395, 0x04},
816 {0x11b8, 0x3a},
817 {0x118b, 0x0e},
818 {0x10f7, 0x05},
819 {0x10f8, 0x14},
820 {0x10fa, 0xff},
821 {0x10f9, 0x00},
822 {0x11ba, 0x0a},
823 {0x11a5, 0x2d},
824 {0x11a6, 0x2d},
825 {0x11a7, 0x3a},
826 {0x11a8, 0x05},
827 {0x11a9, 0x04},
828 {0x11aa, 0x3f},
829 {0x11af, 0x28},
830 {0x11b0, 0xd8},
831 {0x11b1, 0x14},
832 {0x11b2, 0xec},
833 {0x11b3, 0x32},
834 {0x11b4, 0xdd},
835 {0x11b5, 0x32},
836 {0x11b6, 0xdd},
837 {0x10e0, 0x2c},
838 {0x11bc, 0x40},
839 {0x11bd, 0x01},
840 {0x11be, 0xf0},
841 {0x11bf, 0x00},
842 {0x118c, 0x1f},
843 {0x118d, 0x1f},
844 {0x118e, 0x1f},
845 {0x118f, 0x1f},
846 {0x1180, 0x01},
847 {0x1181, 0x00},
848 {0x1182, 0x01},
849 {0x1183, 0x00},
850 {0x1184, 0x50},
851 {0x1185, 0x80},
854 __u8 qtable1[64] = {
855 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
856 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
857 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
858 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
859 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
860 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
861 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
862 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54
865 __u8 qtable2[64] = {
866 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
867 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
868 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
869 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
870 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
871 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
872 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
873 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
876 for (i = 0; i < ARRAY_SIZE(regs); i++) {
877 reg = regs[i][0];
878 value = regs[i][1];
879 ret = usb_sn9c20x_control_write(dev, reg, &value, 1);
880 if (ret < 0) {
881 UDIA_INFO("Bridge Init Error (%d). line %d\n", ret, i);
882 goto err;
886 ret = usb_sn9c20x_control_write(dev, 0x1100, qtable1, 64);
887 if (ret < 0)
888 goto err;
890 ret = usb_sn9c20x_control_write(dev, 0x1140, qtable2, 64);
891 if (ret < 0)
892 goto err;
894 dev->camera.set_contrast = sn9c20x_set_contrast;
895 dev->camera.set_brightness = sn9c20x_set_brightness;
896 dev->camera.set_gamma = sn9c20x_set_gamma;
897 dev->camera.set_sharpness = sn9c20x_set_sharpness;
898 dev->camera.set_red_gain = sn9c20x_set_red_gain;
899 dev->camera.set_blue_gain = sn9c20x_set_blue_gain;
901 ret = sn9c20x_i2c_initialize(dev);
902 if (ret < 0)
903 goto err;
905 ret = sn9c20x_initialize_sensor(dev);
906 return 0;
908 err:
909 UDIA_ERROR("Device Init failed (%d)!\n", ret);
910 return ret;