6 * @brief Common functions and data for the Sonix SN9C20x webcam bridge chips.
8 * @note Copyright (C) Dave Neuer
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
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>
34 * @brief This function initializes the SN9C20x bridge,
35 * these are the bare minimum writes that must be done for
36 * the bridge to work correctly.
40 * @param dev Pointer to the device
42 * @return Zero for success or error value
45 int sn9c20x_initialize(struct usb_microdia
*dev
)
102 __u8 lens_gain
[48] = {
103 0x10, 0x21, 0x34, 0x40, 0x47, 0x4f, 0x57, 0x5f,
104 0x64, 0x68, 0x6d, 0x73, 0x79, 0x80, 0x89, 0x97,
105 0x0d, 0x1c, 0x2a, 0x33, 0x38, 0x3d, 0x44, 0x4a,
106 0x4e, 0x52, 0x56, 0x5b, 0x61, 0x68, 0x6f, 0x7a,
107 0x0d, 0x1a, 0x2a, 0x31, 0x36, 0x3b, 0x41, 0x47,
108 0x4a, 0x4e, 0x53, 0x58, 0x5d, 0x64, 0x6b, 0x76
112 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
113 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
114 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
115 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
116 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
117 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
118 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
119 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54
123 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
124 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
125 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
126 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
127 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
128 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
129 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
130 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
133 for (i
= 0; i
< ARRAY_SIZE(regs
); i
++) {
136 ret
= usb_microdia_control_write(dev
, reg
, &value
, 1);
138 UDIA_INFO("Bridge Init Error (%d). line %d\n", ret
, i
);
143 ret
= usb_microdia_control_write(dev
, 0x1100, qtable1
, 64);
147 ret
= usb_microdia_control_write(dev
, 0x1140, qtable2
, 64);
151 ret
= sn9c20x_i2c_initialize(dev
);
155 dev
->camera
.enable_video
= sn9c20x_enable_video
;
157 dev
->camera
.set_contrast
= sn9c20x_set_contrast
;
158 dev
->camera
.set_brightness
= sn9c20x_set_brightness
;
159 dev
->camera
.set_gamma
= sn9c20x_set_gamma
;
160 dev
->camera
.set_sharpness
= sn9c20x_set_sharpness
;
161 dev
->camera
.set_rgb_gain
= sn9c20x_set_rgb_gain
;
163 sensor
= dev_microdia_probe_sensor(dev
);
164 if (sensor
== UNKNOWN_SENSOR
) {
165 UDIA_ERROR("Failed to detect sensor.\n");
169 dev
->camera
.sensor
= sensor
;
171 if (!(sensor
== OV9650_SENSOR
|| sensor
== OV9655_SENSOR
))
172 memset(lens_gain
, 0x00, 48);
174 ret
= usb_microdia_control_write(dev
, 0x11c0, lens_gain
, 48);
181 UDIA_ERROR("Devive Init failed (%d)!\n", ret
);
186 * @brief Switch Video stream on and off
188 * @param dev Pointer to device structure
189 * @param enable On or off
191 * @author Brian Johnson
193 * @return 0 or negative error code
196 int sn9c20x_enable_video(struct usb_microdia
*dev
, int enable
)
201 ret
= usb_microdia_control_read(dev
, 0x1061, &value
, 1);
207 ret
= usb_microdia_control_write(dev
, 0x1061, &value
, 1);
210 ret
= usb_microdia_control_write(dev
, 0x1061, &value
, 1);
215 ret
= sn9c20x_set_LEDs(dev
, enable
);
221 * @brief Switch LEDs on and off
223 * @param dev Pointer to device structure
224 * @param enable On or off
228 * @return 0 or negative error code
231 int sn9c20x_set_LEDs(struct usb_microdia
*dev
, int enable
)
236 int sensor
= dev
->camera
.sensor
;
288 ret
= usb_microdia_control_write(dev
, 0x1006, led
, 2);
294 * @brief Initializes Micro-Controller's I2C interface
298 * @param dev Pointer to the device
300 * @return Zero (success) or negative (USB-error value)
303 int sn9c20x_i2c_initialize(struct usb_microdia
*dev
)
308 buf
[0] = dev
->camera
.sensor_flags
;
309 buf
[1] = dev
->camera
.sensor_slave_address
;
317 /* Initialize I2C registers to avoid getting no ACK at first I2C operation: */
318 /* Get green diagonal bands w/o this dummy write, Bridge does not know sensor address ? */
319 ret
= usb_microdia_control_write(dev
, 0x10c0, buf
, 9);
327 * @brief Wait until the I2C slave is ready for the next operation
329 * @param dev Pointer to the device
333 * @return Zero for success or a negative error value
336 int sn9c20x_i2c_ack_wait(struct usb_microdia
*dev
, bool highspeed
, bool *slave_error
)
340 int delay
= highspeed
? 100 : 400;
342 for (i
= 0; i
< 5; i
++) {
343 ret
= usb_microdia_control_read(dev
, 0x10c0, &readbuf
, 1);
347 else if (readbuf
& SN9C20X_I2C_ERROR
) {
349 /* probably should come up w/ an error value and
350 * return it via the error return */
352 } else if (readbuf
& SN9C20X_I2C_READY
)
361 * @brief Read up to 5 bytes of data from an I2C slave
363 * @param dev Pointer to the device
364 * @param nbytes Number of bytes to read
365 * @param address The address of the register on the slave to read
366 * @param result A pointer to the location at which the result should be stored
368 * @return Zero for success or a negative error value
371 int sn9c20x_read_i2c_data(struct usb_microdia
*dev
, __u8 nbytes
,
372 __u8 address
, __u8
*result
)
377 if (!dev
|| nbytes
> 4)
380 /* first, we must do a dummy write of just the address */
381 ret
= sn9c20x_write_i2c_data(dev
, 0, address
, NULL
);
386 /* now we issue the same command but with the read bit set
387 * and no slave register address */
388 dev
->camera
.sensor_flags
|= SN9C20X_I2C_READ
;
389 ret
= sn9c20x_write_i2c_data(dev
, nbytes
- 1, 0, row
);
390 dev
->camera
.sensor_flags
&= ~SN9C20X_I2C_READ
;
394 /* finally, ask the bridge for the data */
395 ret
= usb_microdia_control_read(dev
, 0x10c2, row
, 5);
399 for (i
= 0, j
= 5 - nbytes
; i
< nbytes
; i
++, j
++)
406 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
408 * @param dev Pointer to the device
409 * @param datalen Number of 16bit values to read
410 * @param address The address of the register on the slave to read
411 * @param result A pointer to the location at which the result should be stored
413 * @return Zero for success or a negative error value
416 int sn9c20x_read_i2c_data16(struct usb_microdia
*dev
, __u8 datalen
,
417 __u8 address
, __u16
*result
)
425 ret
= sn9c20x_read_i2c_data(dev
, 2*datalen
, address
, result8
);
426 for (k
= 0; k
< datalen
; k
++)
427 result
[k
] = (result8
[k
*2] << 8) | result8
[k
*2+1];
431 static const char *wasread
= "read from";
432 static const char *waswrite
= "write to";
435 * @brief Write up to 5 bytes of data to an I2C slave
437 * @param dev Pointer to the device
438 * @param nbytes The number of bytes of data
439 * @param address The address of the register on the slave to write
440 * @param data An array containing the data to write
442 * @return Zero for success or a negative error value
445 int sn9c20x_write_i2c_data(struct usb_microdia
*dev
, __u8 nbytes
,
446 __u8 address
, const __u8 data
[nbytes
])
450 bool slave_error
= 0;
452 if (!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
455 /* from the point of view of the bridge, the length
456 * includes the address */
457 row
[0] = dev
->camera
.sensor_flags
| ((nbytes
+ 1) << 4);
458 row
[1] = dev
->camera
.sensor_slave_address
;
460 row
[7] = 0x10; /* I think this means we want an ack */
462 for (i
= 0; i
< 4; i
++)
463 row
[i
+ 3] = i
< nbytes
? data
[i
] : 0;
465 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
466 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
), address
,
467 row
[0], row
[1], row
[2], row
[3], row
[4], row
[5], row
[6], row
[7]);
469 ret
= usb_microdia_control_write(dev
, 0x10c0, row
, 8);
471 ret
= sn9c20x_i2c_ack_wait(dev
,
472 dev
->camera
.sensor_flags
& SN9C20X_I2C_400KHZ
,
476 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
477 dev
->camera
.sensor_slave_address
,
478 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
481 /* there should be no interference with USB errors */
486 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
487 dev
->camera
.sensor_slave_address
,
488 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
497 * @brief Write up to 2 16bit values als single bytes to an I2C slave
499 * @param dev Pointer to the device
500 * @param datalen The number of 16bit data values to write
501 * @param address The address of the register on the slave to write
502 * @param data An array containing the data to write
504 * @return Zero for success or a negative error value
507 int sn9c20x_write_i2c_data16(struct usb_microdia
*dev
, __u8 datalen
,
508 __u8 address
, const __u16 data
[datalen
])
516 for (k
= 0; k
< datalen
; k
++) {
517 data8
[k
*2] = data
[k
] >> 8;
518 data8
[k
*2+1] = data
[k
] & 0xff;
520 ret
= sn9c20x_write_i2c_data(dev
, 2*datalen
, address
, data8
);
525 * @brief Set contrast inside sn9c20x chip
529 * @param dev Pointer to the device
531 * @return Zero (success) or negative (USB-error value)
534 int sn9c20x_set_contrast(struct usb_microdia
*dev
)
536 /* from 0x26 to 0x4b */
537 __u8 brightness_contrast
[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
538 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
539 0xfd, 0x0f, 0x0, 0x0, 0x0};
540 __u8 contrast_val
= (dev
->vsettings
.contrast
>> 8) * 0x25 / 0x100;
541 __u8 brightness_val
= dev
->vsettings
.brightness
>> 8;
543 brightness_val
-= 0x80;
544 brightness_contrast
[18] = brightness_val
;
546 contrast_val
+= 0x26;
547 brightness_contrast
[2] = contrast_val
;
548 brightness_contrast
[0] = 0x13 + (brightness_contrast
[2] - 0x26) * 0x13 / 0x25;
549 brightness_contrast
[4] = 0x7 + (brightness_contrast
[2] - 0x26) * 0x7 / 0x25;
551 return usb_microdia_control_write(dev
, 0x10e1, brightness_contrast
, 21);
555 * @brief Set brightness inside sn9c20x chip
559 * @param dev Pointer to the device
561 * @return Zero (success) or negative (USB-error value)
563 * Wrapper for sn9c20x_set_contrast
566 int sn9c20x_set_brightness(struct usb_microdia
*dev
)
568 return dev_microdia_camera_set_contrast(dev
);
572 * @brief Set gamma inside sn9c20x chip
576 * @param dev Pointer to the device
578 * @return Zero (success) or negative (USB-error value)
581 int sn9c20x_set_gamma(struct usb_microdia
*dev
)
583 int value
= (dev
->vsettings
.whiteness
>> 8) * 0xb8 / 0x100;
586 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
587 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
590 gamma_val
[1] = 0x13 + (value
* (0xcb - 0x13) / 0xb8);
591 gamma_val
[2] = 0x25 + (value
* (0xee - 0x25) / 0xb8);
592 gamma_val
[3] = 0x37 + (value
* (0xfa - 0x37) / 0xb8);
593 gamma_val
[4] = 0x45 + (value
* (0xfc - 0x45) / 0xb8);
594 gamma_val
[5] = 0x55 + (value
* (0xfb - 0x55) / 0xb8);
595 gamma_val
[6] = 0x65 + (value
* (0xfc - 0x65) / 0xb8);
596 gamma_val
[7] = 0x74 + (value
* (0xfd - 0x74) / 0xb8);
597 gamma_val
[8] = 0x83 + (value
* (0xfe - 0x83) / 0xb8);
598 gamma_val
[9] = 0x92 + (value
* (0xfc - 0x92) / 0xb8);
599 gamma_val
[10] = 0xa1 + (value
* (0xfc - 0xa1) / 0xb8);
600 gamma_val
[11] = 0xb0 + (value
* (0xfc - 0xb0) / 0xb8);
601 gamma_val
[12] = 0xbf + (value
* (0xfb - 0xbf) / 0xb8);
602 gamma_val
[13] = 0xce + (value
* (0xfb - 0xce) / 0xb8);
603 gamma_val
[14] = 0xdf + (value
* (0xfd - 0xdf) / 0xb8);
604 gamma_val
[15] = 0xea + (value
* (0xf9 - 0xea) / 0xb8);
605 gamma_val
[16] = 0xf5;
607 r
= usb_microdia_control_write(dev
, 0x1190, gamma_val
, 17);
613 * @brief Set sharpness inside sn9c20x chip
617 * @param dev Pointer to the device
619 * @return Zero (success) or negative (USB-error value)
622 int sn9c20x_set_sharpness(struct usb_microdia
*dev
)
627 ret
= usb_microdia_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
630 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
631 ret
= usb_microdia_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
639 * @brief Set colour gain inside sn9c20x chip
641 * @author Brian Johnson
643 * @param dev Pointer to the device
645 * @return Zero (success) or negative (USB-error value)
648 int sn9c20x_set_rgb_gain(struct usb_microdia
*dev
)
653 memcpy(&val
, &(dev
->vsettings
.rgb_gain
), 4);
654 ret
= usb_microdia_control_write(dev
, SN9C20X_RED_GAIN
, val
, 4);
662 * @brief Calculate closest resolution to input from application
664 * @author Brian Johnson
666 * @param dev Pointer to the device structure
667 * @param width Requested width of video stream
668 * @param height Requested height of video stream
670 * @retval width Closest possible width of video stream
671 * @retval height Closest possible height of video stream
673 * @return Number of the
676 int sn9c20x_get_closest_resolution(struct usb_microdia
*dev
,
677 int *width
, int *height
)
681 for (i
= dev
->camera
.nmodes
- 1; i
>= 0; i
--) {
682 if (*width
>= dev
->camera
.modes
[i
].width
683 && *height
>= dev
->camera
.modes
[i
].height
)
687 *width
= dev
->camera
.modes
[i
].width
;
688 *height
= dev
->camera
.modes
[i
].height
;
694 * @brief Set resolution inside sn9c20x chip
696 * @author Brian Johnson
698 * @param dev Pointer to the device
700 * @param height Height
705 int sn9c20x_set_resolution(struct usb_microdia
*dev
,
706 int width
, int height
)
712 struct microdia_video_resolution
*mode
;
714 ret
= sn9c20x_get_closest_resolution(dev
, &width
, &height
);
716 mode
= &dev
->camera
.modes
[ret
];
718 dev
->vsettings
.format
.width
= mode
->width
;
719 dev
->vsettings
.format
.height
= mode
->height
;
721 clrwindow
[0] = 0; clrwindow
[1] = mode
->width
>> 2;
722 clrwindow
[2] = 0; clrwindow
[3] = mode
->height
>> 1;
723 clrwindow
[4] = ((mode
->width
>> 10) & 0x01) |
724 ((mode
->height
>> 10) & 0x06);
727 if (clrwindow
[4] & 0x01)
730 window
[0] = mode
->window
[0] & 0xff; window
[1] = mode
->window
[0] >> 8;
731 window
[2] = mode
->window
[1] & 0xff; window
[3] = mode
->window
[1] >> 8;
732 window
[4] = mode
->window
[2] >> 4; window
[5] = mode
->window
[3] >> 3;
734 usb_microdia_control_write(dev
, 0x10fb, clrwindow
, 5);
735 usb_microdia_control_write(dev
, 0x1180, window
, 6);
736 usb_microdia_control_write(dev
, SN9C20X_SCALE
, &scale
, 1);
738 UDIA_DEBUG("Set mode [%dx%d]\n", mode
->width
, mode
->height
);
743 int sn9c20x_set_raw(struct usb_microdia
*dev
)
746 usb_microdia_control_write(dev
, 0x10e0, &value
, 1);
750 int sn9c20x_set_jpeg(struct usb_microdia
*dev
)
753 usb_microdia_control_write(dev
, 0x10e0, &value
, 1);
758 * @brief Set exposure inside sn9c20x chip
762 * @returns 0 or negative error value
766 int sn9c20x_set_exposure(struct usb_microdia
*dev
)
770 int exposure
= dev
->vsettings
.exposure
;
772 buf
= (__u8
) (exposure
* 0x00ff / 0xffff) & 0x00ff;
774 /* exposure can't be 0 - below 0x04 the image freezes */
778 /* write new value to register 0x118a */
779 ret
= usb_microdia_control_write(dev
, 0x118a, &buf
, 1);
781 UDIA_ERROR("Error: setting exposure failed: "
782 "error while writing to register 0x118a\n");