7 * @brief Common functions and data for the Sonix SN9C20x webcam bridge chips.
9 * @note Copyright (C) Dave Neuer
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include <linux/delay.h>
29 #include <linux/errno.h>
30 #include <linux/string.h>
35 int sn9c20x_i2c_ack_wait(struct usb_microdia
*, bool, bool *);
38 struct sn9c20x_win_size
{
44 } sn9c20x_win_sizes
[] = {
50 .scale
= SN9C20X_1_4_SCALE
,
57 .scale
= SN9C20X_1_2_SCALE
,
64 .scale
= SN9C20X_NO_SCALE
,
70 * @brief Initializes IC2-registers 0x10c0-0x10c7
72 * @param dev Pointer to the device
73 * @param flags The appropriate flags for bus speed and physical connection
74 * @param slave The id of the I2C slave device
76 * @return Zero (success) or negative (USB-error value)
79 int sn9c20x_initialize_i2c(struct usb_microdia
* dev
, __u8 flags
, __u8 slave
)
84 buf
[0] = 0x81 & flags
;
92 ret
= usb_microdia_control_write(dev
, 0x10c0, buf
, 8);
100 * @brief Read up to 5 bytes of data from an I2C slave
102 * @param dev Pointer to the device
103 * @param slave The id of the I2C slave device
104 * @param nbytes Number of bytes to read
105 * @param address The address of the register on the slave to read
106 * @param flags The appropriate flags for bus speed and physical connection
107 * @param result A pointer to the location at which the result should be stored
109 * @return Zero for success or a negative error value
112 int sn9c20x_read_i2c_data(struct usb_microdia
* dev
, __u8 slave
, __u8 nbytes
, __u8 address
,
113 __u8 flags
, __u8
* result
)
118 if(!dev
|| nbytes
> 4)
121 /* first, we must do a dummy write of just the address */
122 ret
= sn9c20x_write_i2c_data(dev
, slave
, 0, address
, flags
, NULL
);
127 /* now we issue the same command but with the read bit set
128 * and no slave register address */
129 ret
= sn9c20x_write_i2c_data(dev
, slave
, nbytes
- 1, 0, flags
|
130 SN9C20X_I2C_READ
, row
);
134 /* finally, ask the bridge for the data */
135 ret
= usb_microdia_control_read(dev
, 0x10c2, row
, 5);
139 for(i
= 0, j
= 5 - nbytes
; i
< nbytes
; i
++, j
++)
146 * @brief Read up to 4 bytes of data from an I2C slave an return them as 16bit values
148 * @param dev Pointer to the device
149 * @param slave The id of the I2C slave device
150 * @param datalen Number of 16bit values to read
151 * @param address The address of the register on the slave to read
152 * @param flags The appropriate flags for bus speed and physical connection
153 * @param result A pointer to the location at which the result should be stored
155 * @return Zero for success or a negative error value
158 int sn9c20x_read_i2c_data16(struct usb_microdia
* dev
, __u8 slave
, __u8 datalen
, __u8 address
,
159 __u8 flags
, __u16
* result
)
165 if(datalen
> 2) return -EINVAL
;
166 ret
= sn9c20x_read_i2c_data(dev
, slave
, 2*datalen
, address
, flags
, result8
);
167 for (k
=0; k
<datalen
; k
++)
168 result
[k
] = (result8
[k
*2] << 8) | result8
[k
*2+1];
172 static const char *wasread
= "read from";
173 static const char *waswrite
= "write to";
176 * @brief Write up to 5 bytes of data to an I2C slave
178 * @param dev Pointer to the device
179 * @param slave The id of the I2C slave device
180 * @param nbytes The number of bytes of data
181 * @param address The address of the register on the slave to write
182 * @param flags The appropriate flags for bus speed and physical connection
183 * @param data An array containing the data to write
185 * @return Zero for success or a negative error value
188 int sn9c20x_write_i2c_data(struct usb_microdia
* dev
, __u8 slave
, __u8 nbytes
,
189 __u8 address
, __u8 flags
, const __u8 data
[nbytes
])
193 bool slave_error
= 0;
195 if(!dev
|| (nbytes
> 0 && !data
) || nbytes
> 4)
198 /* from the point of view of the bridge, the length
199 * includes the address */
200 row
[0] = flags
| ((nbytes
+ 1) << 4);
203 row
[7] = 0x10; /* I think this means we want an ack */
205 for(i
= 0; i
< 4; i
++)
206 row
[i
+ 3] = i
< nbytes
? data
[i
] : 0;
208 UDIA_DEBUG("I2C %s %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n",
209 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
), address
,
210 row
[0], row
[1], row
[2], row
[3], row
[4], row
[5], row
[6], row
[7]);
212 ret
= usb_microdia_control_write(dev
, 0x10c0, row
, 8);
214 ret
= sn9c20x_i2c_ack_wait(dev
, flags
& SN9C20X_I2C_400KHZ
,
218 UDIA_ERROR("I2C slave 0x%02x returned error during %s address 0x%02x\n",
220 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
222 return -1000; // there should be no interference with USB errors
227 UDIA_ERROR("No ack from I2C slave 0x%02x for %s address 0x%02x\n",
229 (flags
& SN9C20X_I2C_READ
? wasread
: waswrite
),
238 * @brief Write up to 2 16bit values als single bytes to an I2C slave
240 * @param dev Pointer to the device
241 * @param slave The id of the I2C slave device
242 * @param datalen The number of 16bit data values to write
243 * @param address The address of the register on the slave to write
244 * @param flags The appropriate flags for bus speed and physical connection
245 * @param data An array containing the data to write
247 * @return Zero for success or a negative error value
250 int sn9c20x_write_i2c_data16(struct usb_microdia
* dev
, __u8 slave
, __u8 datalen
,
251 __u8 address
, __u8 flags
, const __u16 data
[datalen
])
257 if(datalen
> 2) return -EINVAL
;
258 for (k
=0; k
<datalen
; k
++)
260 data8
[k
*2] = data
[k
] >> 8;
261 data8
[k
*2+1] = data
[k
] & 0xff;
263 ret
= sn9c20x_write_i2c_data(dev
, slave
, 2*datalen
, address
, flags
, data8
);
268 * @brief Wait until the I2C slave is ready for the next operation
270 * @param dev Pointer to the device
274 * @return Zero for success or a negative error value
277 int sn9c20x_i2c_ack_wait(struct usb_microdia
*dev
, bool highspeed
, bool * slave_error
)
281 int delay
= highspeed
? 100 : 400;
283 for(i
= 0; i
< 5; i
++) {
284 ret
= usb_microdia_control_read(dev
, 0x10c0, &readbuf
, 1);
288 else if(readbuf
& SN9C20X_I2C_ERROR
) {
290 /* probably should come up w/ an error value and
291 * return it via the error return */
294 else if(readbuf
& SN9C20X_I2C_READY
)
304 int sn9c20x_set_contrast(struct usb_microdia
*dev
)
306 /* from 0x26 to 0x4b */
307 __u8 brightness_contrast
[21] = {0x16, 0x0, 0x2b, 0x0, 0x8, 0x0, 0xf6, 0x0f,
308 0xd2, 0x0f, 0x38, 0x0, 0x34, 0x0, 0xcf, 0x0f,
309 0xfd, 0x0f, 0x0, 0x0, 0x0};
310 __u8 contrast_val
= (dev
->vsettings
.contrast
>> 8) * 0x25 / 0x100;
311 __u8 brightness_val
= dev
->vsettings
.brightness
>> 8;
313 brightness_val
-= 0x80;
314 brightness_contrast
[18] = brightness_val
;
316 contrast_val
+= 0x26;
317 brightness_contrast
[2] = contrast_val
;
318 brightness_contrast
[0] = 0x13 + (brightness_contrast
[2] - 0x26) * 0x13 / 0x25;
319 brightness_contrast
[4] = 0x7 + (brightness_contrast
[2] - 0x26) * 0x7 / 0x25;
321 return usb_microdia_control_write(dev
, 0x10e1, brightness_contrast
, 21);
324 int sn9c20x_set_brightness(struct usb_microdia
*dev
)
326 return dev_microdia_camera_set_contrast(dev
);
329 int sn9c20x_set_gamma(struct usb_microdia
*dev
)
331 int value
= (dev
->vsettings
.whiteness
>> 8) * 0xb8 / 0x100;
334 __u8 gamma_val
[17] = {0x0a, 0x13, 0x25, 0x37, 0x45, 0x55, 0x65, 0x74,
335 0x83, 0x92, 0xa1, 0xb0, 0xbf, 0xce, 0xdf, 0xea, 0xf5};
338 gamma_val
[1] = 0x13 + (value
* (0xcb - 0x13) / 0xb8);
339 gamma_val
[2] = 0x25 + (value
* (0xee - 0x25) / 0xb8);
340 gamma_val
[3] = 0x37 + (value
* (0xfa - 0x37) / 0xb8);
341 gamma_val
[4] = 0x45 + (value
* (0xfc - 0x45) / 0xb8);
342 gamma_val
[5] = 0x55 + (value
* (0xfb - 0x55) / 0xb8);
343 gamma_val
[6] = 0x65 + (value
* (0xfc - 0x65) / 0xb8);
344 gamma_val
[7] = 0x74 + (value
* (0xfd - 0x74) / 0xb8);
345 gamma_val
[8] = 0x83 + (value
* (0xfe - 0x83) / 0xb8);
346 gamma_val
[9] = 0x92 + (value
* (0xfc - 0x92) / 0xb8);
347 gamma_val
[10] = 0xa1 + (value
* (0xfc - 0xa1) / 0xb8);
348 gamma_val
[11] = 0xb0 + (value
* (0xfc - 0xb0) / 0xb8);
349 gamma_val
[12] = 0xbf + (value
* (0xfb - 0xbf) / 0xb8);
350 gamma_val
[13] = 0xce + (value
* (0xfb - 0xce) / 0xb8);
351 gamma_val
[14] = 0xdf + (value
* (0xfd - 0xdf) / 0xb8);
352 gamma_val
[15] = 0xea + (value
* (0xf9 - 0xea) / 0xb8);
353 gamma_val
[16] = 0xf5;
355 r
= usb_microdia_control_write(dev
, 0x1190, gamma_val
, 17);
360 int sn9c20x_set_sharpness(struct usb_microdia
*dev
)
365 ret
= usb_microdia_control_read(dev
, SN9C20X_SHARPNESS
, val
, 1);
368 val
[0] = (val
[0] & 0xc0) | (dev
->vsettings
.sharpness
& 0x3f);
369 ret
= usb_microdia_control_write(dev
, SN9C20X_SHARPNESS
, val
, 1);
376 int sn9c20x_set_rgb_gain(struct usb_microdia
*dev
)
381 memcpy(&val
, &(dev
->vsettings
.rgb_gain
), 4);
382 ret
= usb_microdia_control_write(dev
, SN9C20X_RED_GAIN
, val
, 4);
389 int sn9c20x_get_closest_resolution(int *width
, int *height
)
393 for (i
= ARRAY_SIZE(sn9c20x_win_sizes
) - 1; i
>= 0; i
--) {
394 if (*width
>= sn9c20x_win_sizes
[i
].hsize
395 && *height
>= sn9c20x_win_sizes
[i
].vsize
)
399 *width
= sn9c20x_win_sizes
[i
].hsize
;
400 *height
= sn9c20x_win_sizes
[i
].vsize
;
405 int sn9c20x_set_resolution(struct usb_microdia
*dev
,
406 int width
, int height
)
411 struct sn9c20x_win_size
*wsize
;
413 ret
= sn9c20x_get_closest_resolution(&width
, &height
);
414 wsize
= &sn9c20x_win_sizes
[ret
];
416 dev
->vsettings
.format
.width
= width
;
417 dev
->vsettings
.format
.height
= height
;
419 window
[0] = wsize
->hstart
>> 2; window
[1] = wsize
->hsize
>> 2;
420 window
[2] = wsize
->vstart
>> 1; window
[3] = wsize
->vsize
>> 1;
423 usb_microdia_control_read(dev
, SN9C20X_SCALE
, &scale
, 1);
424 scale
= (scale
& 0xCF) | wsize
->scale
;
425 usb_microdia_control_write(dev
, SN9C20X_HSTART
, window
, 5);
426 usb_microdia_control_write(dev
, SN9C20X_SCALE
, &scale
, 1);
429 * queue.frame_size is only ever used in isoc_handler
430 * to test if the ISOC data is the correct size
431 * This modificate pre calculates this rather
432 * than doing the calculating in the isoc_handler
434 dev
->queue
.frame_size
= width
* height
+
435 (width
* height
) / dev
->frame_size_divisor
;
438 UDIA_DEBUG("Set mode [%dx%d]\n", width
, height
);