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>
31 #include "sn9c20x-bridge.h"
33 int sn9c20x_set_camera_control(struct usb_sn9c20x
*dev
,
34 __u32 control
, __u32 value
)
38 case V4L2_CID_CONTRAST
:
39 if (dev
->camera
.set_contrast
) {
40 dev
->vsettings
.contrast
= value
;
41 ret
= dev
->camera
.set_contrast(dev
);
44 case V4L2_CID_BRIGHTNESS
:
45 if (dev
->camera
.set_brightness
) {
46 dev
->vsettings
.brightness
= value
;
47 ret
= dev
->camera
.set_brightness(dev
);
51 if (dev
->camera
.set_gamma
) {
52 dev
->vsettings
.gamma
= value
;
53 ret
= dev
->camera
.set_gamma(dev
);
56 case V4L2_CID_SHARPNESS
:
57 if (dev
->camera
.set_sharpness
) {
58 dev
->vsettings
.sharpness
= value
;
59 ret
= dev
->camera
.set_sharpness(dev
);
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
);
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
);
75 if (dev
->camera
.set_hvflip
) {
76 dev
->vsettings
.hflip
= value
;
77 ret
= dev
->camera
.set_hvflip(dev
);
81 if (dev
->camera
.set_hvflip
) {
82 dev
->vsettings
.vflip
= value
;
83 ret
= dev
->camera
.set_hvflip(dev
);
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
);
92 case V4L2_CID_EXPOSURE
:
93 if (dev
->camera
.set_exposure
) {
94 dev
->vsettings
.exposure
= value
;
95 ret
= dev
->camera
.set_exposure(dev
);
99 if (dev
->camera
.set_gain
) {
100 dev
->vsettings
.gain
= value
;
101 ret
= dev
->camera
.set_gain(dev
);
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
);
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
);
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
)
136 ret
= usb_sn9c20x_control_read(dev
, 0x1061, &value
, 1);
142 ret
= usb_sn9c20x_control_write(dev
, 0x1061, &value
, 1);
145 ret
= usb_sn9c20x_control_write(dev
, 0x1061, &value
, 1);
148 sn9c20x_set_LEDs(dev
, enable
);
154 * @brief Switch LEDs on and off
156 * @param dev Pointer to device structure
157 * @param enable On or off
161 * @return 0 or negative error code
164 int sn9c20x_set_LEDs(struct usb_sn9c20x
*dev
, int enable
)
169 int sensor
= dev
->camera
.sensor
;
211 ret
= usb_sn9c20x_control_write(dev
, 0x1006, led
, 2);
217 * @brief Initializes Micro-Controller's I2C interface
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
)
231 dev
->camera
.i2c_flags
= SN9C20X_I2C_2WIRE
;
233 buf
[0] = dev
->camera
.i2c_flags
;
234 buf
[1] = dev
->camera
.address
;
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);
252 * @brief Wait until the I2C slave is ready for the next operation
254 * @param dev Pointer to the device
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
)
265 int delay
= highspeed
? 100 : 400;
267 for (i
= 0; i
< 5; i
++) {
268 ret
= usb_sn9c20x_control_read(dev
, 0x10c0, &readbuf
, 1);
272 else if (readbuf
& SN9C20X_I2C_ERROR
) {
274 /* probably should come up w/ an error value and
275 * return it via the error return */
277 } else if (readbuf
& SN9C20X_I2C_READY
)
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
)
302 if (!dev
|| nbytes
> 4)
305 /* first, we must do a dummy write of just the address */
306 ret
= sn9c20x_write_i2c_data(dev
, 0, address
, NULL
);
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
;
319 /* finally, ask the bridge for the data */
320 ret
= usb_sn9c20x_control_read(dev
, 0x10c2, row
, 5);
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
++)
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
)
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];
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
)
379 bool slave_error
= 0;
381 if (!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
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
;
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],
399 ret
= usb_sn9c20x_control_write(dev
, 0x10c0, row
, 8);
401 ret
= sn9c20x_i2c_ack_wait(dev
,
402 dev
->camera
.i2c_flags
& SN9C20X_I2C_400KHZ
,
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
);
410 /* there should be no interference with USB errors */
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
);
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
])
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
);
452 int sn9c20x_write_i2c_array(struct usb_sn9c20x
*dev
,
453 struct sn9c20x_i2c_regs
*regs
, int bits16
)
460 if (regs
[i
].address
== 0xff)
463 value16
= regs
[i
].value
;
464 ret
= sn9c20x_write_i2c_data16(dev
, 1,
465 regs
[i
].address
, &value16
);
467 value8
= (__u8
)regs
[i
].value
;
468 ret
= sn9c20x_write_i2c_data(dev
, 1,
469 regs
[i
].address
, &value8
);
478 * @brief Set contrast inside sn9c20x chip
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
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
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;
539 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
540 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
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);
566 * @brief Set sharpness inside sn9c20x chip
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
)
580 ret
= usb_sn9c20x_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
583 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
584 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
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
)
606 val
= dev
->vsettings
.red_gain
;
607 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_RED_GAIN
, &val
, 1);
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
)
629 val
= dev
->vsettings
.blue_gain
;
630 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_BLUE_GAIN
, &val
, 1);
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
)
658 for (i
= SN9C20X_N_MODES
- 1; i
>= 0; i
--) {
659 if (*width
>= sn9c20x_modes
[i
].width
660 && *height
>= sn9c20x_modes
[i
].height
)
664 *width
= sn9c20x_modes
[i
].width
;
665 *height
= sn9c20x_modes
[i
].height
;
671 * @brief Set resolution inside sn9c20x chip
673 * @author Brian Johnson
675 * @param dev Pointer to the device
677 * @param height Height
682 int sn9c20x_set_resolution(struct usb_sn9c20x
*dev
,
683 int width
, int height
)
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
;
699 clrwindow
[1] = mode
->width
>> 2;
701 clrwindow
[3] = mode
->height
>> 1;
702 clrwindow
[4] = ((mode
->width
>> 10) & 0x01) |
703 ((mode
->height
>> 10) & 0x06);
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
);
724 int sn9c20x_set_format(struct usb_sn9c20x
*dev
, __u32 format
)
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
);
747 void sn9c20x_set_raw(struct usb_sn9c20x
*dev
)
751 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
754 void sn9c20x_set_jpeg(struct usb_sn9c20x
*dev
)
758 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
761 void sn9c20x_set_yuv420(struct usb_sn9c20x
*dev
)
765 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
768 void sn9c20x_set_yuv422(struct usb_sn9c20x
*dev
)
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.
782 * @param dev Pointer to the device
784 * @return Zero for success or error value
787 int sn9c20x_initialize(struct usb_sn9c20x
*dev
)
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
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
++) {
879 ret
= usb_sn9c20x_control_write(dev
, reg
, &value
, 1);
881 UDIA_INFO("Bridge Init Error (%d). line %d\n", ret
, i
);
886 ret
= usb_sn9c20x_control_write(dev
, 0x1100, qtable1
, 64);
890 ret
= usb_sn9c20x_control_write(dev
, 0x1140, qtable2
, 64);
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
);
905 ret
= sn9c20x_initialize_sensor(dev
);
909 UDIA_ERROR("Device Init failed (%d)!\n", ret
);