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
;
219 ret
= usb_sn9c20x_control_write(dev
, 0x1006, led
, 2);
225 * @brief Initializes Micro-Controller's I2C interface
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
)
239 dev
->camera
.i2c_flags
= SN9C20X_I2C_2WIRE
;
241 buf
[0] = dev
->camera
.i2c_flags
;
242 buf
[1] = dev
->camera
.address
;
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);
260 * @brief Wait until the I2C slave is ready for the next operation
262 * @param dev Pointer to the device
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
)
273 int delay
= highspeed
? 100 : 400;
275 for (i
= 0; i
< 5; i
++) {
276 ret
= usb_sn9c20x_control_read(dev
, 0x10c0, &readbuf
, 1);
280 else if (readbuf
& SN9C20X_I2C_ERROR
) {
282 /* probably should come up w/ an error value and
283 * return it via the error return */
285 } else if (readbuf
& SN9C20X_I2C_READY
)
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
)
310 if (!dev
|| nbytes
> 4)
313 /* first, we must do a dummy write of just the address */
314 ret
= sn9c20x_write_i2c_data(dev
, 0, address
, NULL
);
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
;
327 /* finally, ask the bridge for the data */
328 ret
= usb_sn9c20x_control_read(dev
, 0x10c2, row
, 5);
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
++)
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
)
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];
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
)
387 bool slave_error
= 0;
389 if (!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
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
;
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],
407 ret
= usb_sn9c20x_control_write(dev
, 0x10c0, row
, 8);
409 ret
= sn9c20x_i2c_ack_wait(dev
,
410 dev
->camera
.i2c_flags
& SN9C20X_I2C_400KHZ
,
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
);
418 /* there should be no interference with USB errors */
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
);
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
])
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
);
460 int sn9c20x_write_i2c_array(struct usb_sn9c20x
*dev
,
461 struct sn9c20x_i2c_regs
*regs
, int bits16
)
468 if (regs
[i
].address
== 0xff)
471 value16
= regs
[i
].value
;
472 ret
= sn9c20x_write_i2c_data16(dev
, 1,
473 regs
[i
].address
, &value16
);
475 value8
= (__u8
)regs
[i
].value
;
476 ret
= sn9c20x_write_i2c_data(dev
, 1,
477 regs
[i
].address
, &value8
);
486 * @brief Set contrast inside sn9c20x chip
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
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
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;
547 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
548 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
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);
574 * @brief Set sharpness inside sn9c20x chip
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
)
588 ret
= usb_sn9c20x_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
591 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
592 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
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
)
614 val
= dev
->vsettings
.red_gain
;
615 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_RED_GAIN
, &val
, 1);
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
)
637 val
= dev
->vsettings
.blue_gain
;
638 ret
= usb_sn9c20x_control_write(dev
, SN9C20X_BLUE_GAIN
, &val
, 1);
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
)
666 for (i
= SN9C20X_N_MODES
- 1; i
>= 0; i
--) {
667 if (*width
>= sn9c20x_modes
[i
].width
668 && *height
>= sn9c20x_modes
[i
].height
)
672 *width
= sn9c20x_modes
[i
].width
;
673 *height
= sn9c20x_modes
[i
].height
;
679 * @brief Set resolution inside sn9c20x chip
681 * @author Brian Johnson
683 * @param dev Pointer to the device
685 * @param height Height
690 int sn9c20x_set_resolution(struct usb_sn9c20x
*dev
,
691 int width
, int height
)
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
;
707 clrwindow
[1] = mode
->width
>> 2;
709 clrwindow
[3] = mode
->height
>> 1;
710 clrwindow
[4] = ((mode
->width
>> 10) & 0x01) |
711 ((mode
->height
>> 10) & 0x06);
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
);
732 int sn9c20x_set_format(struct usb_sn9c20x
*dev
, __u32 format
)
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
);
755 void sn9c20x_set_raw(struct usb_sn9c20x
*dev
)
759 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
762 void sn9c20x_set_jpeg(struct usb_sn9c20x
*dev
)
766 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
769 void sn9c20x_set_yuv420(struct usb_sn9c20x
*dev
)
773 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
776 void sn9c20x_set_yuv422(struct usb_sn9c20x
*dev
)
780 usb_sn9c20x_control_write(dev
, 0x10e0, &value
, 1);
784 * @brief This function initializes the SN9C20x bridge,
785 * these are the bare minimum writes that must be done for
786 * the bridge to work correctly.
790 * @param dev Pointer to the device
792 * @return Zero for success or error value
795 int sn9c20x_initialize(struct usb_sn9c20x
*dev
)
863 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
864 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
865 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
866 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
867 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
868 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
869 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
870 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54
874 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
875 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
876 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
877 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
878 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
879 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
880 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
881 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
884 for (i
= 0; i
< ARRAY_SIZE(regs
); i
++) {
887 ret
= usb_sn9c20x_control_write(dev
, reg
, &value
, 1);
889 UDIA_INFO("Bridge Init Error (%d). line %d\n", ret
, i
);
894 ret
= usb_sn9c20x_control_write(dev
, 0x1100, qtable1
, 64);
898 ret
= usb_sn9c20x_control_write(dev
, 0x1140, qtable2
, 64);
902 dev
->camera
.set_contrast
= sn9c20x_set_contrast
;
903 dev
->camera
.set_brightness
= sn9c20x_set_brightness
;
904 dev
->camera
.set_gamma
= sn9c20x_set_gamma
;
905 dev
->camera
.set_sharpness
= sn9c20x_set_sharpness
;
906 dev
->camera
.set_red_gain
= sn9c20x_set_red_gain
;
907 dev
->camera
.set_blue_gain
= sn9c20x_set_blue_gain
;
909 ret
= sn9c20x_i2c_initialize(dev
);
913 ret
= sn9c20x_initialize_sensor(dev
);
917 UDIA_ERROR("Device Init failed (%d)!\n", ret
);