Fixes missing termination in init block for the hv7131r sensor.
[microdia.git] / sn9c20x-bridge.c
blobf5d77b8ed68e6623853fa735a3317407fb8dee4d
1 /**
2 * @file sn9c20x-bridge.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 dev->vsettings.auto_exposure = value;
112 ret = 0;
113 if (dev->camera.set_auto_exposure)
114 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 Josua Grawitter
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 case MT9V111_SENSOR:
189 led[0] = 0xc0;
190 led[1] = 0xe0;
191 break;
192 default:
193 led[0] = 0x00;
194 led[1] = 0x20;
196 } else {
197 switch (sensor) {
198 case HV7131R_SENSOR:
199 case MT9M001_SENSOR:
200 case MT9V011_SENSOR:
201 led[0] = 0xa0;
202 led[1] = 0xa0;
203 break;
204 case MT9M111_SENSOR:
205 case OV7660_SENSOR:
206 led[0] = 0x20;
207 led[1] = 0x60;
208 break;
209 case MT9V111_SENSOR:
210 led[0] = 0x80;
211 led[1] = 0xe0;
212 break;
213 default:
214 led[0] = 0x20;
215 led[1] = 0x20;
219 ret = usb_sn9c20x_control_write(dev, 0x1006, led, 2);
221 return ret;
225 * @brief Initializes Micro-Controller's I2C interface
227 * @author Neekhil
229 * @param dev Pointer to the device
231 * @return Zero (success) or negative (USB-error value)
234 int sn9c20x_i2c_initialize(struct usb_sn9c20x *dev)
236 __u8 buf[9];
237 int ret;
239 dev->camera.i2c_flags = SN9C20X_I2C_2WIRE;
241 buf[0] = dev->camera.i2c_flags;
242 buf[1] = dev->camera.address;
243 buf[2] = 0x00;
244 buf[3] = 0x00;
245 buf[4] = 0x00;
246 buf[5] = 0x00;
247 buf[6] = 0x00;
248 buf[7] = 0x00;
249 buf[8] = 0x03;
250 /* Initialize I2C registers to avoid getting no ACK at first I2C operation: */
251 /* Get green diagonal bands w/o this dummy write, Bridge does not know sensor address ? */
252 ret = usb_sn9c20x_control_write(dev, 0x10c0, buf, 9);
253 if (ret < 0)
254 return ret;
255 else
256 return 0;
260 * @brief Wait until the I2C slave is ready for the next operation
262 * @param dev Pointer to the device
263 * @param highspeed
264 * @param slave_error
266 * @return Zero for success or a negative error value
269 int sn9c20x_i2c_ack_wait(struct usb_sn9c20x *dev, bool highspeed, bool *slave_error)
271 int ret, i;
272 __u8 readbuf;
273 int delay = highspeed ? 100 : 400;
275 for (i = 0; i < 5; i++) {
276 ret = usb_sn9c20x_control_read(dev, 0x10c0, &readbuf, 1);
278 if (unlikely(ret < 0))
279 return ret;
280 else if (readbuf & SN9C20X_I2C_ERROR) {
281 *slave_error = 1;
282 /* probably should come up w/ an error value and
283 * return it via the error return */
284 return 0;
285 } else if (readbuf & SN9C20X_I2C_READY)
286 return 0;
287 else
288 udelay(delay);
290 return -EBUSY;
294 * @brief Read up to 5 bytes of data from an I2C slave
296 * @param dev Pointer to the device
297 * @param nbytes Number of bytes to read
298 * @param address The address of the register on the slave to read
299 * @param result A pointer to the location at which the result should be stored
301 * @return Zero for success or a negative error value
304 int sn9c20x_read_i2c_data(struct usb_sn9c20x *dev, __u8 nbytes,
305 __u8 address, __u8 *result)
307 int ret, i, j;
308 __u8 row[5];
310 if (!dev || nbytes > 4)
311 return -EINVAL;
313 /* first, we must do a dummy write of just the address */
314 ret = sn9c20x_write_i2c_data(dev, 0, address, NULL);
315 if (unlikely(ret < 0))
316 return ret;
318 memset(row, 0, 5);
319 /* now we issue the same command but with the read bit set
320 * and no slave register address */
321 dev->camera.i2c_flags |= SN9C20X_I2C_READ;
322 ret = sn9c20x_write_i2c_data(dev, nbytes - 1, 0, row);
323 dev->camera.i2c_flags &= ~SN9C20X_I2C_READ;
324 if (unlikely(ret < 0))
325 return ret;
327 /* finally, ask the bridge for the data */
328 ret = usb_sn9c20x_control_read(dev, 0x10c2, row, 5);
329 if (unlikely(ret < 0))
330 return ret;
332 UDIA_DEBUG("I2C read: %02x %02x %02x %02x %02x %02x\n",
333 address, row[0], row[1], row[2], row[3], row[4]);
335 for (i = 0, j = 5 - nbytes; i < nbytes; i++, j++)
336 result[i] = row[j];
338 return 0;
342 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
344 * @param dev Pointer to the device
345 * @param datalen Number of 16bit values to read
346 * @param address The address of the register on the slave to read
347 * @param result A pointer to the location at which the result should be stored
349 * @return Zero for success or a negative error value
352 int sn9c20x_read_i2c_data16(struct usb_sn9c20x *dev, __u8 datalen,
353 __u8 address, __u16 *result)
355 __u8 result8[4];
356 __u8 k;
357 int ret;
359 if (datalen > 2)
360 return -EINVAL;
361 ret = sn9c20x_read_i2c_data(dev, 2*datalen, address, result8);
362 for (k = 0; k < datalen; k++)
363 result[k] = (result8[k*2] << 8) | result8[k*2+1];
364 return ret;
367 static const char *wasread = "read from";
368 static const char *waswrite = "write to";
371 * @brief Write up to 5 bytes of data to an I2C slave
373 * @param dev Pointer to the device
374 * @param nbytes The number of bytes of data
375 * @param address The address of the register on the slave to write
376 * @param data An array containing the data to write
377 * @param last_byte The byte to be sent as last byte of control sequence
379 * @return Zero for success or a negative error value
382 int sn9c20x_write_i2c_data_ext(struct usb_sn9c20x *dev, __u8 nbytes,
383 __u8 address, const __u8 data[nbytes], __u8 last_byte)
385 int ret, i;
386 __u8 row[8];
387 bool slave_error = 0;
389 if (!dev || (nbytes > 0 && !data) || nbytes > 4)
390 return -EINVAL;
392 /* from the point of view of the bridge, the length
393 * includes the address */
394 row[0] = dev->camera.i2c_flags | ((nbytes + 1) << 4);
395 row[1] = dev->camera.address;
396 row[2] = address;
397 row[7] = last_byte;
399 for (i = 0; i < 4; i++)
400 row[i + 3] = i < nbytes ? data[i] : 0;
402 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
403 (dev->camera.i2c_flags & SN9C20X_I2C_READ ? wasread : waswrite),
404 address, row[0], row[1], row[2], row[3], row[4], row[5], row[6],
405 row[7]);
407 ret = usb_sn9c20x_control_write(dev, 0x10c0, row, 8);
408 if (likely(ret >= 0))
409 ret = sn9c20x_i2c_ack_wait(dev,
410 dev->camera.i2c_flags & SN9C20X_I2C_400KHZ,
411 &slave_error);
413 if (slave_error) {
414 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
415 dev->camera.address, (dev->camera.i2c_flags &
416 SN9C20X_I2C_READ ? wasread : waswrite), address);
417 return -1000;
418 /* there should be no interference with USB errors */
421 if (ret < 0) {
422 /* we got no ack */
423 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
424 dev->camera.address, (dev->camera.i2c_flags &
425 SN9C20X_I2C_READ ? wasread : waswrite), address);
426 return ret;
429 return 0;
433 * @brief Write up to 2 16bit values als single bytes to an I2C slave
435 * @param dev Pointer to the device
436 * @param datalen The number of 16bit data values to write
437 * @param address The address of the register on the slave to write
438 * @param data An array containing the data to write
440 * @return Zero for success or a negative error value
443 int sn9c20x_write_i2c_data16(struct usb_sn9c20x *dev, __u8 datalen,
444 __u8 address, const __u16 data[datalen])
446 __u8 data8[4];
447 __u8 k;
448 int ret;
450 if (datalen > 2)
451 return -EINVAL;
452 for (k = 0; k < datalen; k++) {
453 data8[k*2] = data[k] >> 8;
454 data8[k*2+1] = data[k] & 0xff;
456 ret = sn9c20x_write_i2c_data(dev, 2*datalen, address, data8);
457 return ret;
460 int sn9c20x_write_i2c_array(struct usb_sn9c20x *dev,
461 struct sn9c20x_i2c_regs *regs, int bits16)
463 int i;
464 int ret = 0;
465 __u16 value16;
466 __u8 value8;
467 for (i = 0;; i++) {
468 if (regs[i].address == 0xff)
469 break;
470 if (bits16) {
471 value16 = regs[i].value;
472 ret = sn9c20x_write_i2c_data16(dev, 1,
473 regs[i].address, &value16);
474 } else {
475 value8 = (__u8)regs[i].value;
476 ret = sn9c20x_write_i2c_data(dev, 1,
477 regs[i].address, &value8);
479 if (unlikely(ret < 0))
480 return ret;
482 return ret;
486 * @brief Set contrast inside sn9c20x chip
488 * @author Comer352l
490 * @param dev Pointer to the device
492 * @return Zero (success) or negative (USB-error value)
495 int sn9c20x_set_contrast(struct usb_sn9c20x *dev)
497 /* from 0x26 to 0x4b */
498 __u8 brightness_contrast[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
499 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
500 0xfd, 0x0f, 0x0, 0x0, 0x0};
501 __u8 contrast_val = (dev->vsettings.contrast) * 0x25 / 0x100;
502 __u8 brightness_val = dev->vsettings.brightness;
504 brightness_val -= 0x80;
505 brightness_contrast[18] = brightness_val;
507 contrast_val += 0x26;
508 brightness_contrast[2] = contrast_val;
509 brightness_contrast[0] = 0x13 + (brightness_contrast[2] - 0x26) * 0x13 / 0x25;
510 brightness_contrast[4] = 0x7 + (brightness_contrast[2] - 0x26) * 0x7 / 0x25;
512 return usb_sn9c20x_control_write(dev, 0x10e1, brightness_contrast, 21);
516 * @brief Set brightness inside sn9c20x chip
518 * @author Comer352l
520 * @param dev Pointer to the device
522 * @return Zero (success) or negative (USB-error value)
524 * Wrapper for sn9c20x_set_contrast
527 int sn9c20x_set_brightness(struct usb_sn9c20x *dev)
529 return sn9c20x_set_contrast(dev);
533 * @brief Set gamma inside sn9c20x chip
535 * @author Comer352l
537 * @param dev Pointer to the device
539 * @return Zero (success) or negative (USB-error value)
542 int sn9c20x_set_gamma(struct usb_sn9c20x *dev)
544 int value = (dev->vsettings.gamma) * 0xb8 / 0x100;
545 int r = 0;
547 __u8 gamma_val[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
548 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
550 gamma_val[0] = 0x0a;
551 gamma_val[1] = 0x13 + (value * (0xcb - 0x13) / 0xb8);
552 gamma_val[2] = 0x25 + (value * (0xee - 0x25) / 0xb8);
553 gamma_val[3] = 0x37 + (value * (0xfa - 0x37) / 0xb8);
554 gamma_val[4] = 0x45 + (value * (0xfc - 0x45) / 0xb8);
555 gamma_val[5] = 0x55 + (value * (0xfb - 0x55) / 0xb8);
556 gamma_val[6] = 0x65 + (value * (0xfc - 0x65) / 0xb8);
557 gamma_val[7] = 0x74 + (value * (0xfd - 0x74) / 0xb8);
558 gamma_val[8] = 0x83 + (value * (0xfe - 0x83) / 0xb8);
559 gamma_val[9] = 0x92 + (value * (0xfc - 0x92) / 0xb8);
560 gamma_val[10] = 0xa1 + (value * (0xfc - 0xa1) / 0xb8);
561 gamma_val[11] = 0xb0 + (value * (0xfc - 0xb0) / 0xb8);
562 gamma_val[12] = 0xbf + (value * (0xfb - 0xbf) / 0xb8);
563 gamma_val[13] = 0xce + (value * (0xfb - 0xce) / 0xb8);
564 gamma_val[14] = 0xdf + (value * (0xfd - 0xdf) / 0xb8);
565 gamma_val[15] = 0xea + (value * (0xf9 - 0xea) / 0xb8);
566 gamma_val[16] = 0xf5;
568 r = usb_sn9c20x_control_write(dev, 0x1190, gamma_val, 17);
570 return r;
574 * @brief Set sharpness inside sn9c20x chip
576 * @author Comer352l
578 * @param dev Pointer to the device
580 * @return Zero (success) or negative (USB-error value)
583 int sn9c20x_set_sharpness(struct usb_sn9c20x *dev)
585 __u8 val[1];
586 int ret;
588 ret = usb_sn9c20x_control_read(dev, SN9C20X_SHARPNESS, val, 1);
589 if (ret < 0)
590 return ret;
591 val[0] = (val[0] & 0xc0) | (dev->vsettings.sharpness & 0x3f);
592 ret = usb_sn9c20x_control_write(dev, SN9C20X_SHARPNESS, val, 1);
593 if (ret < 0)
594 return ret;
595 else
596 return 0;
600 * @brief Set red gain inside sn9c20x chip
602 * @author Brian Johnson
604 * @param dev Pointer to the device
606 * @return Zero (success) or negative (USB-error value)
609 int sn9c20x_set_red_gain(struct usb_sn9c20x *dev)
611 __u8 val;
612 int ret;
614 val = dev->vsettings.red_gain;
615 ret = usb_sn9c20x_control_write(dev, SN9C20X_RED_GAIN, &val, 1);
616 if (ret < 0)
617 return ret;
618 else
619 return 0;
623 * @brief Set blue gain inside sn9c20x chip
625 * @author Brian Johnson
627 * @param dev Pointer to the device
629 * @return Zero (success) or negative (USB-error value)
632 int sn9c20x_set_blue_gain(struct usb_sn9c20x *dev)
634 __u8 val;
635 int ret;
637 val = dev->vsettings.blue_gain;
638 ret = usb_sn9c20x_control_write(dev, SN9C20X_BLUE_GAIN, &val, 1);
639 if (ret < 0)
640 return ret;
641 else
642 return 0;
647 * @brief Calculate closest resolution to input from application
649 * @author Brian Johnson
651 * @param dev Pointer to the device structure
652 * @param width Requested width of video stream
653 * @param height Requested height of video stream
655 * @retval width Closest possible width of video stream
656 * @retval height Closest possible height of video stream
658 * @return Number of the
661 int sn9c20x_get_closest_resolution(struct usb_sn9c20x *dev,
662 int *width, int *height)
664 int i;
666 for (i = SN9C20X_N_MODES - 1; i >= 0; i--) {
667 if (*width >= sn9c20x_modes[i].width
668 && *height >= sn9c20x_modes[i].height)
669 break;
672 *width = sn9c20x_modes[i].width;
673 *height = sn9c20x_modes[i].height;
675 return i;
679 * @brief Set resolution inside sn9c20x chip
681 * @author Brian Johnson
683 * @param dev Pointer to the device
684 * @param width Width
685 * @param height Height
687 * @return 0
690 int sn9c20x_set_resolution(struct usb_sn9c20x *dev,
691 int width, int height)
693 int ret;
694 __u8 scale;
695 __u8 window[6];
696 __u8 clrwindow[5];
697 struct sn9c20x_video_mode *mode;
699 ret = sn9c20x_get_closest_resolution(dev, &width, &height);
701 mode = &sn9c20x_modes[ret];
703 dev->vsettings.format.width = mode->width;
704 dev->vsettings.format.height = mode->height;
706 clrwindow[0] = 0;
707 clrwindow[1] = mode->width >> 2;
708 clrwindow[2] = 0;
709 clrwindow[3] = mode->height >> 1;
710 clrwindow[4] = ((mode->width >> 10) & 0x01) |
711 ((mode->height >> 8) & 0x06);
713 scale = mode->scale;
715 window[0] = (mode->window[0] + dev->camera.hstart) & 0xff;
716 window[1] = (mode->window[0] + dev->camera.hstart) >> 8;
717 window[2] = (mode->window[1] + dev->camera.vstart) & 0xff;
718 window[3] = (mode->window[1] + dev->camera.vstart) >> 8;
719 window[4] = mode->window[2] >> 4;
720 window[5] = mode->window[3] >> 3;
722 usb_sn9c20x_control_write(dev, 0x10fb, clrwindow, 5);
723 usb_sn9c20x_control_write(dev, 0x1180, window, 6);
724 usb_sn9c20x_control_write(dev, SN9C20X_SCALE, &scale, 1);
726 UDIA_DEBUG("Set mode [%dx%d]\n", mode->width, mode->height);
728 return 0;
732 int sn9c20x_set_format(struct usb_sn9c20x *dev, __u32 format)
734 int i;
735 int ret = -EINVAL;
736 for (i = 0; i < SN9C20X_N_FMTS; i++) {
737 if (sn9c20x_fmts[i].pix_fmt == format) {
738 dev->vsettings.format.bytesperline =
739 dev->vsettings.format.width *
740 sn9c20x_fmts[i].depth / 8;
741 dev->vsettings.format.sizeimage =
742 dev->vsettings.format.height *
743 dev->vsettings.format.bytesperline;
744 dev->vsettings.format.pixelformat = format;
745 dev->vsettings.format.colorspace = V4L2_COLORSPACE_SRGB;
746 dev->vsettings.format.priv = 0;
747 sn9c20x_fmts[i].set_format(dev);
748 ret = 0;
749 break;
752 return ret;
755 void sn9c20x_set_raw(struct usb_sn9c20x *dev)
757 __u8 value;
758 value = 0x2d;
759 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
760 UDIA_INFO("Using raw output format\n");
763 void sn9c20x_set_jpeg(struct usb_sn9c20x *dev)
765 __u8 value;
766 value = 0x2c;
767 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
768 UDIA_INFO("Using jpeg output format\n");
771 void sn9c20x_set_yuv420(struct usb_sn9c20x *dev)
773 __u8 value;
774 value = 0x2f;
775 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
776 UDIA_INFO("Using yuv420 output format\n");
779 void sn9c20x_set_yuv422(struct usb_sn9c20x *dev)
781 __u8 value;
782 value = 0x2e;
783 usb_sn9c20x_control_write(dev, 0x10e0, &value, 1);
784 UDIA_INFO("Using yuv422 output format\n");
788 * @brief This function initializes the SN9C20x bridge,
789 * these are the bare minimum writes that must be done for
790 * the bridge to work correctly.
792 * @author Neekhil
794 * @param dev Pointer to the device
796 * @return Zero for success or error value
799 int sn9c20x_initialize(struct usb_sn9c20x *dev)
801 int ret, i;
803 __u16 reg;
804 __u8 value;
806 __u16 regs[][2] = {
807 {0x1000, 0x78},
808 {0x1001, 0x40},
809 {0x1002, 0x1c},
810 {0x1020, 0x80},
811 {0x1061, 0x01},
812 {0x1067, 0x40},
813 {0x1068, 0x30},
814 {0x1069, 0x20},
815 {0x106a, 0x10},
816 {0x106b, 0x08},
817 {0x1188, 0x87},
818 {0x11a1, 0x00},
819 {0x11a2, 0x00},
820 {0x11a3, 0x6a},
821 {0x11a4, 0x50},
822 {0x11ab, 0x00},
823 {0x11ac, 0x00},
824 {0x11ad, 0x50},
825 {0x11ae, 0x3c},
826 {0x118a, 0x04},
827 {0x0395, 0x04},
828 {0x11b8, 0x3a},
829 {0x118b, 0x0e},
830 {0x10f7, 0x05},
831 {0x10f8, 0x14},
832 {0x10fa, 0xff},
833 {0x10f9, 0x00},
834 {0x11ba, 0x0a},
835 {0x11a5, 0x2d},
836 {0x11a6, 0x2d},
837 {0x11a7, 0x3a},
838 {0x11a8, 0x05},
839 {0x11a9, 0x04},
840 {0x11aa, 0x3f},
841 {0x11af, 0x28},
842 {0x11b0, 0xd8},
843 {0x11b1, 0x14},
844 {0x11b2, 0xec},
845 {0x11b3, 0x32},
846 {0x11b4, 0xdd},
847 {0x11b5, 0x32},
848 {0x11b6, 0xdd},
849 {0x10e0, 0x2c},
850 {0x11bc, 0x40},
851 {0x11bd, 0x01},
852 {0x11be, 0xf0},
853 {0x11bf, 0x00},
854 {0x118c, 0x1f},
855 {0x118d, 0x1f},
856 {0x118e, 0x1f},
857 {0x118f, 0x1f},
858 {0x1180, 0x01},
859 {0x1181, 0x00},
860 {0x1182, 0x01},
861 {0x1183, 0x00},
862 {0x1184, 0x50},
863 {0x1185, 0x80},
866 __u8 qtable1[64] = {
867 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
868 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
869 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
870 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
871 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
872 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
873 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
874 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54
877 __u8 qtable2[64] = {
878 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
879 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
880 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
881 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
882 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
883 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
884 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
885 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
888 for (i = 0; i < ARRAY_SIZE(regs); i++) {
889 reg = regs[i][0];
890 value = regs[i][1];
891 ret = usb_sn9c20x_control_write(dev, reg, &value, 1);
892 if (unlikely(ret < 0)) {
893 UDIA_INFO("Bridge Init Error (%d). line %d\n", ret, i);
894 goto err;
898 ret = usb_sn9c20x_control_write(dev, 0x1100, qtable1, 64);
899 if (ret < 0)
900 goto err;
902 ret = usb_sn9c20x_control_write(dev, 0x1140, qtable2, 64);
903 if (ret < 0)
904 goto err;
906 #ifdef CONFIG_SN9C20X_EVDEV
907 ret = usb_sn9c20x_control_read(dev, 0x1005, &dev->input_gpio, 1);
908 if (ret < 0)
909 goto err;
911 dev->input_gpio = ~dev->input_gpio;
912 #endif
914 dev->camera.set_contrast = sn9c20x_set_contrast;
915 dev->camera.set_brightness = sn9c20x_set_brightness;
916 dev->camera.set_gamma = sn9c20x_set_gamma;
917 dev->camera.set_sharpness = sn9c20x_set_sharpness;
918 dev->camera.set_red_gain = sn9c20x_set_red_gain;
919 dev->camera.set_blue_gain = sn9c20x_set_blue_gain;
921 ret = sn9c20x_i2c_initialize(dev);
922 if (ret < 0)
923 goto err;
925 ret = sn9c20x_initialize_sensor(dev);
926 return ret;
928 err:
929 UDIA_ERROR("Device Init failed (%d)!\n", ret);
930 return ret;
933 int sn9c20x_reset_device(struct usb_sn9c20x *dev)
935 if (sn9c20x_initialize(dev) < 0)
936 return -EINVAL;
938 sn9c20x_set_camera_control(dev, V4L2_CID_HFLIP,
939 dev->vsettings.hflip);
940 sn9c20x_set_camera_control(dev, V4L2_CID_VFLIP,
941 dev->vsettings.vflip);
942 sn9c20x_set_camera_control(dev, V4L2_CID_GAIN,
943 dev->vsettings.gain);
944 sn9c20x_set_camera_control(dev, V4L2_CID_BRIGHTNESS,
945 dev->vsettings.brightness);
946 sn9c20x_set_camera_control(dev, V4L2_CID_CONTRAST,
947 dev->vsettings.contrast);
948 sn9c20x_set_camera_control(dev, V4L2_CID_GAMMA,
949 dev->vsettings.gamma);
950 sn9c20x_set_camera_control(dev, V4L2_CID_SHARPNESS,
951 dev->vsettings.sharpness);
952 sn9c20x_set_camera_control(dev, V4L2_CID_RED_BALANCE,
953 dev->vsettings.red_gain);
954 sn9c20x_set_camera_control(dev, V4L2_CID_BLUE_BALANCE,
955 dev->vsettings.blue_gain);
956 sn9c20x_set_camera_control(dev, V4L2_CID_EXPOSURE_AUTO,
957 dev->vsettings.auto_exposure);
958 sn9c20x_set_camera_control(dev, V4L2_CID_AUTOGAIN,
959 dev->vsettings.auto_gain);
960 sn9c20x_set_camera_control(dev, V4L2_CID_AUTO_WHITE_BALANCE,
961 dev->vsettings.auto_whitebalance);
962 sn9c20x_set_camera_control(dev, V4L2_CID_EXPOSURE,
963 dev->vsettings.exposure);
965 sn9c20x_set_resolution(dev, dev->vsettings.format.width,
966 dev->vsettings.format.height);
968 sn9c20x_set_format(dev, dev->vsettings.format.pixelformat);
970 return 0;