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");
170 if (!(sensor
== OV9650_SENSOR
|| sensor
== OV9655_SENSOR
))
171 memset(lens_gain
, 0x00, 48);
173 ret
= usb_microdia_control_write(dev
, 0x11c0, lens_gain
, 48);
180 UDIA_ERROR("Devive Init failed (%d)!\n", ret
);
185 * @brief Switch Video stream on and off
187 * @param dev Pointer to device structure
188 * @param enable On or off
190 * @author Brian Johnson
192 * @return 0 or negative error code
195 int sn9c20x_enable_video(struct usb_microdia
*dev
, int enable
)
200 ret
= usb_microdia_control_read(dev
, 0x1061, &value
, 1);
206 ret
= usb_microdia_control_write(dev
, 0x1061, &value
, 1);
209 ret
= usb_microdia_control_write(dev
, 0x1061, &value
, 1);
214 ret
= sn9c20x_set_LEDs(dev
, enable
);
220 * @brief Switch LEDs on and off
222 * @param dev Pointer to device structure
223 * @param enable On or off
227 * @return 0 or negative error code
230 int sn9c20x_set_LEDs(struct usb_microdia
*dev
, int enable
)
235 int sensor
= dev_microdia_probe_sensor(dev
);
287 ret
= usb_microdia_control_write(dev
, 0x1006, led
, 2);
293 * @brief Initializes Micro-Controller's I2C interface
297 * @param dev Pointer to the device
299 * @return Zero (success) or negative (USB-error value)
302 int sn9c20x_i2c_initialize(struct usb_microdia
*dev
)
307 buf
[0] = dev
->camera
.sensor_flags
;
308 buf
[1] = dev
->camera
.sensor_slave_address
;
316 /* Initialize I2C registers to avoid getting no ACK at first I2C operation: */
317 /* Get green diagonal bands w/o this dummy write, Bridge does not know sensor address ? */
318 ret
= usb_microdia_control_write(dev
, 0x10c0, buf
, 9);
326 * @brief Wait until the I2C slave is ready for the next operation
328 * @param dev Pointer to the device
332 * @return Zero for success or a negative error value
335 int sn9c20x_i2c_ack_wait(struct usb_microdia
*dev
, bool highspeed
, bool *slave_error
)
339 int delay
= highspeed
? 100 : 400;
341 for (i
= 0; i
< 5; i
++) {
342 ret
= usb_microdia_control_read(dev
, 0x10c0, &readbuf
, 1);
346 else if (readbuf
& SN9C20X_I2C_ERROR
) {
348 /* probably should come up w/ an error value and
349 * return it via the error return */
351 } else if (readbuf
& SN9C20X_I2C_READY
)
360 * @brief Read up to 5 bytes of data from an I2C slave
362 * @param dev Pointer to the device
363 * @param nbytes Number of bytes to read
364 * @param address The address of the register on the slave to read
365 * @param result A pointer to the location at which the result should be stored
367 * @return Zero for success or a negative error value
370 int sn9c20x_read_i2c_data(struct usb_microdia
*dev
, __u8 nbytes
,
371 __u8 address
, __u8
*result
)
376 if (!dev
|| nbytes
> 4)
379 /* first, we must do a dummy write of just the address */
380 ret
= sn9c20x_write_i2c_data(dev
, 0, address
, NULL
);
385 /* now we issue the same command but with the read bit set
386 * and no slave register address */
387 dev
->camera
.sensor_flags
|= SN9C20X_I2C_READ
;
388 ret
= sn9c20x_write_i2c_data(dev
, nbytes
- 1, 0, row
);
389 dev
->camera
.sensor_flags
&= ~SN9C20X_I2C_READ
;
393 /* finally, ask the bridge for the data */
394 ret
= usb_microdia_control_read(dev
, 0x10c2, row
, 5);
398 for (i
= 0, j
= 5 - nbytes
; i
< nbytes
; i
++, j
++)
405 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
407 * @param dev Pointer to the device
408 * @param datalen Number of 16bit values to read
409 * @param address The address of the register on the slave to read
410 * @param result A pointer to the location at which the result should be stored
412 * @return Zero for success or a negative error value
415 int sn9c20x_read_i2c_data16(struct usb_microdia
*dev
, __u8 datalen
,
416 __u8 address
, __u16
*result
)
424 ret
= sn9c20x_read_i2c_data(dev
, 2*datalen
, address
, result8
);
425 for (k
= 0; k
< datalen
; k
++)
426 result
[k
] = (result8
[k
*2] << 8) | result8
[k
*2+1];
430 static const char *wasread
= "read from";
431 static const char *waswrite
= "write to";
434 * @brief Write up to 5 bytes of data to an I2C slave
436 * @param dev Pointer to the device
437 * @param nbytes The number of bytes of data
438 * @param address The address of the register on the slave to write
439 * @param data An array containing the data to write
441 * @return Zero for success or a negative error value
444 int sn9c20x_write_i2c_data(struct usb_microdia
*dev
, __u8 nbytes
,
445 __u8 address
, const __u8 data
[nbytes
])
449 bool slave_error
= 0;
451 if (!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
454 /* from the point of view of the bridge, the length
455 * includes the address */
456 row
[0] = dev
->camera
.sensor_flags
| ((nbytes
+ 1) << 4);
457 row
[1] = dev
->camera
.sensor_slave_address
;
459 row
[7] = 0x10; /* I think this means we want an ack */
461 for (i
= 0; i
< 4; i
++)
462 row
[i
+ 3] = i
< nbytes
? data
[i
] : 0;
464 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
465 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
), address
,
466 row
[0], row
[1], row
[2], row
[3], row
[4], row
[5], row
[6], row
[7]);
468 ret
= usb_microdia_control_write(dev
, 0x10c0, row
, 8);
470 ret
= sn9c20x_i2c_ack_wait(dev
,
471 dev
->camera
.sensor_flags
& SN9C20X_I2C_400KHZ
,
475 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
476 dev
->camera
.sensor_slave_address
,
477 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
480 /* there should be no interference with USB errors */
485 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
486 dev
->camera
.sensor_slave_address
,
487 (dev
->camera
.sensor_flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
496 * @brief Write up to 2 16bit values als single bytes to an I2C slave
498 * @param dev Pointer to the device
499 * @param datalen The number of 16bit data values to write
500 * @param address The address of the register on the slave to write
501 * @param data An array containing the data to write
503 * @return Zero for success or a negative error value
506 int sn9c20x_write_i2c_data16(struct usb_microdia
*dev
, __u8 datalen
,
507 __u8 address
, const __u16 data
[datalen
])
515 for (k
= 0; k
< datalen
; k
++) {
516 data8
[k
*2] = data
[k
] >> 8;
517 data8
[k
*2+1] = data
[k
] & 0xff;
519 ret
= sn9c20x_write_i2c_data(dev
, 2*datalen
, address
, data8
);
524 * @brief Set contrast inside sn9c20x chip
528 * @param dev Pointer to the device
530 * @return Zero (success) or negative (USB-error value)
533 int sn9c20x_set_contrast(struct usb_microdia
*dev
)
535 /* from 0x26 to 0x4b */
536 __u8 brightness_contrast
[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
537 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
538 0xfd, 0x0f, 0x0, 0x0, 0x0};
539 __u8 contrast_val
= (dev
->vsettings
.contrast
>> 8) * 0x25 / 0x100;
540 __u8 brightness_val
= dev
->vsettings
.brightness
>> 8;
542 brightness_val
-= 0x80;
543 brightness_contrast
[18] = brightness_val
;
545 contrast_val
+= 0x26;
546 brightness_contrast
[2] = contrast_val
;
547 brightness_contrast
[0] = 0x13 + (brightness_contrast
[2] - 0x26) * 0x13 / 0x25;
548 brightness_contrast
[4] = 0x7 + (brightness_contrast
[2] - 0x26) * 0x7 / 0x25;
550 return usb_microdia_control_write(dev
, 0x10e1, brightness_contrast
, 21);
554 * @brief Set brightness inside sn9c20x chip
558 * @param dev Pointer to the device
560 * @return Zero (success) or negative (USB-error value)
562 * Wrapper for sn9c20x_set_contrast
565 int sn9c20x_set_brightness(struct usb_microdia
*dev
)
567 return dev_microdia_camera_set_contrast(dev
);
571 * @brief Set gamma inside sn9c20x chip
575 * @param dev Pointer to the device
577 * @return Zero (success) or negative (USB-error value)
580 int sn9c20x_set_gamma(struct usb_microdia
*dev
)
582 int value
= (dev
->vsettings
.whiteness
>> 8) * 0xb8 / 0x100;
585 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
586 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
589 gamma_val
[1] = 0x13 + (value
* (0xcb - 0x13) / 0xb8);
590 gamma_val
[2] = 0x25 + (value
* (0xee - 0x25) / 0xb8);
591 gamma_val
[3] = 0x37 + (value
* (0xfa - 0x37) / 0xb8);
592 gamma_val
[4] = 0x45 + (value
* (0xfc - 0x45) / 0xb8);
593 gamma_val
[5] = 0x55 + (value
* (0xfb - 0x55) / 0xb8);
594 gamma_val
[6] = 0x65 + (value
* (0xfc - 0x65) / 0xb8);
595 gamma_val
[7] = 0x74 + (value
* (0xfd - 0x74) / 0xb8);
596 gamma_val
[8] = 0x83 + (value
* (0xfe - 0x83) / 0xb8);
597 gamma_val
[9] = 0x92 + (value
* (0xfc - 0x92) / 0xb8);
598 gamma_val
[10] = 0xa1 + (value
* (0xfc - 0xa1) / 0xb8);
599 gamma_val
[11] = 0xb0 + (value
* (0xfc - 0xb0) / 0xb8);
600 gamma_val
[12] = 0xbf + (value
* (0xfb - 0xbf) / 0xb8);
601 gamma_val
[13] = 0xce + (value
* (0xfb - 0xce) / 0xb8);
602 gamma_val
[14] = 0xdf + (value
* (0xfd - 0xdf) / 0xb8);
603 gamma_val
[15] = 0xea + (value
* (0xf9 - 0xea) / 0xb8);
604 gamma_val
[16] = 0xf5;
606 r
= usb_microdia_control_write(dev
, 0x1190, gamma_val
, 17);
612 * @brief Set sharpness inside sn9c20x chip
616 * @param dev Pointer to the device
618 * @return Zero (success) or negative (USB-error value)
621 int sn9c20x_set_sharpness(struct usb_microdia
*dev
)
626 ret
= usb_microdia_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
629 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
630 ret
= usb_microdia_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
638 * @brief Set colour gain inside sn9c20x chip
640 * @author Brian Johnson
642 * @param dev Pointer to the device
644 * @return Zero (success) or negative (USB-error value)
647 int sn9c20x_set_rgb_gain(struct usb_microdia
*dev
)
652 memcpy(&val
, &(dev
->vsettings
.rgb_gain
), 4);
653 ret
= usb_microdia_control_write(dev
, SN9C20X_RED_GAIN
, val
, 4);
661 * @brief Calculate closest resolution to input from application
663 * @author Brian Johnson
665 * @param dev Pointer to the device structure
666 * @param width Requested width of video stream
667 * @param height Requested height of video stream
669 * @retval width Closest possible width of video stream
670 * @retval height Closest possible height of video stream
672 * @return Number of the
675 int sn9c20x_get_closest_resolution(struct usb_microdia
*dev
,
676 int *width
, int *height
)
680 for (i
= dev
->camera
.nmodes
- 1; i
>= 0; i
--) {
681 if (*width
>= dev
->camera
.modes
[i
].width
682 && *height
>= dev
->camera
.modes
[i
].height
)
686 *width
= dev
->camera
.modes
[i
].width
;
687 *height
= dev
->camera
.modes
[i
].height
;
693 * @brief Set resolution inside sn9c20x chip
695 * @author Brian Johnson
697 * @param dev Pointer to the device
699 * @param height Height
704 int sn9c20x_set_resolution(struct usb_microdia
*dev
,
705 int width
, int height
)
711 struct microdia_video_resolution
*mode
;
713 ret
= sn9c20x_get_closest_resolution(dev
, &width
, &height
);
715 mode
= &dev
->camera
.modes
[ret
];
717 dev
->vsettings
.format
.width
= mode
->width
;
718 dev
->vsettings
.format
.height
= mode
->height
;
720 clrwindow
[0] = 0; clrwindow
[1] = mode
->width
>> 2;
721 clrwindow
[2] = 0; clrwindow
[3] = mode
->height
>> 1;
722 clrwindow
[4] = ((mode
->width
>> 10) & 0x01) |
723 ((mode
->height
>> 10) & 0x06);
726 if (clrwindow
[4] & 0x01)
729 window
[0] = mode
->window
[0] & 0xff; window
[1] = mode
->window
[0] >> 8;
730 window
[2] = mode
->window
[1] & 0xff; window
[3] = mode
->window
[1] >> 8;
731 window
[4] = mode
->window
[2] >> 4; window
[5] = mode
->window
[3] >> 3;
733 usb_microdia_control_write(dev
, 0x10fb, clrwindow
, 5);
734 usb_microdia_control_write(dev
, 0x1180, window
, 6);
735 usb_microdia_control_write(dev
, SN9C20X_SCALE
, &scale
, 1);
737 UDIA_DEBUG("Set mode [%dx%d]\n", mode
->width
, mode
->height
);
742 int sn9c20x_set_raw(struct usb_microdia
*dev
)
745 usb_microdia_control_write(dev
, 0x10e0, &value
, 1);
749 int sn9c20x_set_jpeg(struct usb_microdia
*dev
)
752 usb_microdia_control_write(dev
, 0x10e0, &value
, 1);
757 * @brief Set exposure inside sn9c20x chip
761 * @returns 0 or negative error value
765 int sn9c20x_set_exposure(struct usb_microdia
*dev
)
769 int exposure
= dev
->vsettings
.exposure
;
771 buf
= (__u8
) (exposure
* 0x00ff / 0xffff) & 0x00ff;
773 /* exposure can't be 0 - below 0x04 the image freezes */
777 /* write new value to register 0x118a */
778 ret
= usb_microdia_control_write(dev
, 0x118a, &buf
, 1);
780 UDIA_ERROR("Error: setting exposure failed: "
781 "error while writing to register 0x118a\n");