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 struct sn9c20x_win_size
{
40 } sn9c20x_win_sizes
[] = {
46 .scale
= SN9C20X_1_4_SCALE
,
53 .scale
= SN9C20X_1_2_SCALE
,
60 .scale
= SN9C20X_NO_SCALE
,
65 * @brief This function initializes the SN9C20x bridge, these are the bare minimum writes that must be done for the bridge to work correctly.
69 * @param dev Pointer to the device
71 * @return Zero for success or error value
74 int sn9c20x_initialize(struct usb_microdia
*dev
)
80 /* Could these be the bridge ADC controls, video stream seems jagged w/o this write */
83 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
87 /* No video stream if we don't do this. Could be wake up Bridge from soft sleep/suspend? */
90 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
94 /* No video stream w/o this write */
97 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
101 /* Some sort of Brightness control */
104 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
108 /* All 3 writes below are required else NO video stream */
111 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
115 buf
[0] = 0x01; buf
[1] = 0x00; buf
[2] = 0x01; buf
[3] = 0x00; buf
[4] = 0x28;
117 ret
= usb_microdia_control_write(dev
, reg
, buf
, 6);
121 /* Some sort of brightness control */
124 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
128 /* we get only green bands if we don't do this write */
131 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
135 /* Bridge Auto-Exposure setting ? */
138 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
142 /* Bridge Auto-Exposure setting ? */
145 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
151 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
155 /* These 2 writes finally start the video stream */
158 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
163 ret
= usb_microdia_control_write(dev
, reg
, buf
, 1);
170 UDIA_ERROR("r/w in bridge register %x failed (%d)!\n", reg
, ret
);
175 * @brief Initializes Micro-Controller's I2C interface
179 * @param dev Pointer to the device
181 * @return Zero (success) or negative (USB-error value)
184 int sn9c20x_i2c_initialize(struct usb_microdia
*dev
)
189 buf
[0] = dev
->sensor_flags
;
190 buf
[1] = dev
->sensor_slave_address
;
198 /* Initialize I2C registers to avoid getting no ACK at first I2C operation: */
199 /* Get green diagonal bands w/o this dummy write, Bridge does not know sensor address ? */
200 ret
= usb_microdia_control_write(dev
, 0x10c0, buf
, 9);
208 * @brief Wait until the I2C slave is ready for the next operation
210 * @param dev Pointer to the device
214 * @return Zero for success or a negative error value
217 int sn9c20x_i2c_ack_wait(struct usb_microdia
*dev
, bool highspeed
, bool *slave_error
)
221 int delay
= highspeed
? 100 : 400;
223 for (i
= 0; i
< 5; i
++) {
224 ret
= usb_microdia_control_read(dev
, 0x10c0, &readbuf
, 1);
228 else if (readbuf
& SN9C20X_I2C_ERROR
) {
230 /* probably should come up w/ an error value and
231 * return it via the error return */
233 } else if (readbuf
& SN9C20X_I2C_READY
)
242 * @brief Read up to 5 bytes of data from an I2C slave
244 * @param dev Pointer to the device
245 * @param slave The id of the I2C slave device
246 * @param nbytes Number of bytes to read
247 * @param address The address of the register on the slave to read
248 * @param flags The appropriate flags for bus speed and physical connection
249 * @param result A pointer to the location at which the result should be stored
251 * @return Zero for success or a negative error value
254 int sn9c20x_read_i2c_data(struct usb_microdia
*dev
, __u8 slave
, __u8 nbytes
, __u8 address
,
255 __u8 flags
, __u8
*result
)
260 if (!dev
|| nbytes
> 4)
263 /* first, we must do a dummy write of just the address */
264 ret
= sn9c20x_write_i2c_data(dev
, slave
, 0, address
, flags
, NULL
);
269 /* now we issue the same command but with the read bit set
270 * and no slave register address */
271 ret
= sn9c20x_write_i2c_data(dev
, slave
, nbytes
- 1, 0, flags
|
272 SN9C20X_I2C_READ
, row
);
276 /* finally, ask the bridge for the data */
277 ret
= usb_microdia_control_read(dev
, 0x10c2, row
, 5);
281 for (i
= 0, j
= 5 - nbytes
; i
< nbytes
; i
++, j
++)
288 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
290 * @param dev Pointer to the device
291 * @param slave The id of the I2C slave device
292 * @param datalen Number of 16bit values to read
293 * @param address The address of the register on the slave to read
294 * @param flags The appropriate flags for bus speed and physical connection
295 * @param result A pointer to the location at which the result should be stored
297 * @return Zero for success or a negative error value
300 int sn9c20x_read_i2c_data16(struct usb_microdia
*dev
, __u8 slave
, __u8 datalen
, __u8 address
,
301 __u8 flags
, __u16
*result
)
309 ret
= sn9c20x_read_i2c_data(dev
, slave
, 2*datalen
, address
, flags
, result8
);
310 for (k
= 0; k
< datalen
; k
++)
311 result
[k
] = (result8
[k
*2] << 8) | result8
[k
*2+1];
315 static const char *wasread
= "read from";
316 static const char *waswrite
= "write to";
319 * @brief Write up to 5 bytes of data to an I2C slave
321 * @param dev Pointer to the device
322 * @param slave The id of the I2C slave device
323 * @param nbytes The number of bytes of data
324 * @param address The address of the register on the slave to write
325 * @param flags The appropriate flags for bus speed and physical connection
326 * @param data An array containing the data to write
328 * @return Zero for success or a negative error value
331 int sn9c20x_write_i2c_data(struct usb_microdia
*dev
, __u8 slave
, __u8 nbytes
,
332 __u8 address
, __u8 flags
, const __u8 data
[nbytes
])
336 bool slave_error
= 0;
338 if (!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
341 /* from the point of view of the bridge, the length
342 * includes the address */
343 row
[0] = flags
| ((nbytes
+ 1) << 4);
346 row
[7] = 0x10; /* I think this means we want an ack */
348 for (i
= 0; i
< 4; i
++)
349 row
[i
+ 3] = i
< nbytes
? data
[i
] : 0;
351 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
352 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
), address
,
353 row
[0], row
[1], row
[2], row
[3], row
[4], row
[5], row
[6], row
[7]);
355 ret
= usb_microdia_control_write(dev
, 0x10c0, row
, 8);
357 ret
= sn9c20x_i2c_ack_wait(dev
, flags
& SN9C20X_I2C_400KHZ
,
361 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
363 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
366 /* there should be no interference with USB errors */
371 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
373 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
382 * @brief Write up to 2 16bit values als single bytes to an I2C slave
384 * @param dev Pointer to the device
385 * @param slave The id of the I2C slave device
386 * @param datalen The number of 16bit data values to write
387 * @param address The address of the register on the slave to write
388 * @param flags The appropriate flags for bus speed and physical connection
389 * @param data An array containing the data to write
391 * @return Zero for success or a negative error value
394 int sn9c20x_write_i2c_data16(struct usb_microdia
*dev
, __u8 slave
, __u8 datalen
,
395 __u8 address
, __u8 flags
, const __u16 data
[datalen
])
403 for (k
= 0; k
< datalen
; k
++) {
404 data8
[k
*2] = data
[k
] >> 8;
405 data8
[k
*2+1] = data
[k
] & 0xff;
407 ret
= sn9c20x_write_i2c_data(dev
, slave
, 2*datalen
, address
, flags
, data8
);
412 * @brief Set contrast inside sn9c20x chip
416 * @param dev Pointer to the device
418 * @return Zero (success) or negative (USB-error value)
421 int sn9c20x_set_contrast(struct usb_microdia
*dev
)
423 /* from 0x26 to 0x4b */
424 __u8 brightness_contrast
[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
425 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
426 0xfd, 0x0f, 0x0, 0x0, 0x0};
427 __u8 contrast_val
= (dev
->vsettings
.contrast
>> 8) * 0x25 / 0x100;
428 __u8 brightness_val
= dev
->vsettings
.brightness
>> 8;
430 brightness_val
-= 0x80;
431 brightness_contrast
[18] = brightness_val
;
433 contrast_val
+= 0x26;
434 brightness_contrast
[2] = contrast_val
;
435 brightness_contrast
[0] = 0x13 + (brightness_contrast
[2] - 0x26) * 0x13 / 0x25;
436 brightness_contrast
[4] = 0x7 + (brightness_contrast
[2] - 0x26) * 0x7 / 0x25;
438 return usb_microdia_control_write(dev
, 0x10e1, brightness_contrast
, 21);
442 * @brief Set brightness inside sn9c20x chip
446 * @param dev Pointer to the device
448 * @return Zero (success) or negative (USB-error value)
450 * Wrapper for sn9c20x_set_contrast
453 int sn9c20x_set_brightness(struct usb_microdia
*dev
)
455 return dev_microdia_camera_set_contrast(dev
);
459 * @brief Set gamma inside sn9c20x chip
463 * @param dev Pointer to the device
465 * @return Zero (success) or negative (USB-error value)
468 int sn9c20x_set_gamma(struct usb_microdia
*dev
)
470 int value
= (dev
->vsettings
.whiteness
>> 8) * 0xb8 / 0x100;
473 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
474 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
477 gamma_val
[1] = 0x13 + (value
* (0xcb - 0x13) / 0xb8);
478 gamma_val
[2] = 0x25 + (value
* (0xee - 0x25) / 0xb8);
479 gamma_val
[3] = 0x37 + (value
* (0xfa - 0x37) / 0xb8);
480 gamma_val
[4] = 0x45 + (value
* (0xfc - 0x45) / 0xb8);
481 gamma_val
[5] = 0x55 + (value
* (0xfb - 0x55) / 0xb8);
482 gamma_val
[6] = 0x65 + (value
* (0xfc - 0x65) / 0xb8);
483 gamma_val
[7] = 0x74 + (value
* (0xfd - 0x74) / 0xb8);
484 gamma_val
[8] = 0x83 + (value
* (0xfe - 0x83) / 0xb8);
485 gamma_val
[9] = 0x92 + (value
* (0xfc - 0x92) / 0xb8);
486 gamma_val
[10] = 0xa1 + (value
* (0xfc - 0xa1) / 0xb8);
487 gamma_val
[11] = 0xb0 + (value
* (0xfc - 0xb0) / 0xb8);
488 gamma_val
[12] = 0xbf + (value
* (0xfb - 0xbf) / 0xb8);
489 gamma_val
[13] = 0xce + (value
* (0xfb - 0xce) / 0xb8);
490 gamma_val
[14] = 0xdf + (value
* (0xfd - 0xdf) / 0xb8);
491 gamma_val
[15] = 0xea + (value
* (0xf9 - 0xea) / 0xb8);
492 gamma_val
[16] = 0xf5;
494 r
= usb_microdia_control_write(dev
, 0x1190, gamma_val
, 17);
500 * @brief Set sharpness inside sn9c20x chip
504 * @param dev Pointer to the device
506 * @return Zero (success) or negative (USB-error value)
509 int sn9c20x_set_sharpness(struct usb_microdia
*dev
)
514 ret
= usb_microdia_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
517 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
518 ret
= usb_microdia_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
526 * @brief Set colour gain inside sn9c20x chip
528 * @author Brian Johnson
530 * @param dev Pointer to the device
532 * @return Zero (success) or negative (USB-error value)
535 int sn9c20x_set_rgb_gain(struct usb_microdia
*dev
)
540 memcpy(&val
, &(dev
->vsettings
.rgb_gain
), 4);
541 ret
= usb_microdia_control_write(dev
, SN9C20X_RED_GAIN
, val
, 4);
549 * @brief Calculate closest resolution to input from application
551 * @author Brian Johnson
553 * @param width Requested width of video stream
554 * @param height Requested height of video stream
556 * @retval width Closest possible width of video stream
557 * @retval height Closest possible height of video stream
559 * @return Number of the
562 int sn9c20x_get_closest_resolution(int *width
, int *height
)
566 for (i
= ARRAY_SIZE(sn9c20x_win_sizes
) - 1; i
>= 0; i
--) {
567 if (*width
>= sn9c20x_win_sizes
[i
].hsize
568 && *height
>= sn9c20x_win_sizes
[i
].vsize
)
572 *width
= sn9c20x_win_sizes
[i
].hsize
;
573 *height
= sn9c20x_win_sizes
[i
].vsize
;
579 * @brief Set resolution inside sn9c20x chip
581 * @author Brian Johnson
583 * @param dev Pointer to the device
585 * @param height Height
590 int sn9c20x_set_resolution(struct usb_microdia
*dev
,
591 int width
, int height
)
596 struct sn9c20x_win_size
*wsize
;
598 ret
= sn9c20x_get_closest_resolution(&width
, &height
);
599 wsize
= &sn9c20x_win_sizes
[ret
];
601 dev
->vsettings
.format
.width
= width
;
602 dev
->vsettings
.format
.height
= height
;
604 window
[0] = wsize
->hstart
>> 2; window
[1] = wsize
->hsize
>> 2;
605 window
[2] = wsize
->vstart
>> 1; window
[3] = wsize
->vsize
>> 1;
608 usb_microdia_control_read(dev
, SN9C20X_SCALE
, &scale
, 1);
609 scale
= (scale
& 0xCF) | wsize
->scale
;
610 usb_microdia_control_write(dev
, SN9C20X_HSTART
, window
, 5);
611 usb_microdia_control_write(dev
, SN9C20X_SCALE
, &scale
, 1);
614 * queue.frame_size is only ever used in isoc_handler
615 * to test if the ISOC data is the correct size
616 * This modificate pre calculates this rather
617 * than doing the calculating in the isoc_handler
619 dev
->queue
.frame_size
= width
* height
+
620 (width
* height
) / dev
->frame_size_divisor
;
623 UDIA_DEBUG("Set mode [%dx%d]\n", width
, height
);
629 * @brief Set exposure inside sn9c20x chip
633 * @returns 0 or negative error value
637 int sn9c20x_set_exposure(struct usb_microdia
*dev
)
641 int exposure
= dev
->vsettings
.exposure
;
643 buf
= (__u8
) (exposure
* 0x00ff / 0xffff) & 0x00ff;
645 /* exposure can't be 0 - below 0x04 the image freezes */
649 /* write new value to register 0x118a */
650 ret
= usb_microdia_control_write(dev
, 0x118a, &buf
, 1);
652 UDIA_ERROR("Error: setting exposure failed: "
653 "error while writing to register 0x118a\n");