Commit af67d439 authored by Luca Risolia's avatar Luca Risolia Committed by Greg Kroah-Hartman

[PATCH] USB: SN9C10[12] driver update

This patch brings the driver up to the first stable version.

Changes:

* Remove "redblue" entry under /sys
* Better coding style for comments
* Fix the image downscaling factor calculation
* Fix default color settings for some image sensors
* Fix TAS5130D1B image sensor support
* Other small cleanups
* Remove "EXPERIMENTAL" symbol from KConfig
+ Add support for PAS202BCB sensor (thanks to
  Carlos Eduardo Medaglia Dyonisio)
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent dfe49c2b
...@@ -142,7 +142,7 @@ Description: Debugging information level, from 0 to 3: ...@@ -142,7 +142,7 @@ Description: Debugging information level, from 0 to 3:
1 = critical errors 1 = critical errors
2 = significant informations 2 = significant informations
3 = more verbose messages 3 = more verbose messages
Level 3 is useful for testing only, when just one device Level 3 is useful for testing only, when only one device
is used. is used.
Default: 2 Default: 2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -153,12 +153,10 @@ Default: 2 ...@@ -153,12 +153,10 @@ Default: 2
It is possible to read and write both the SN9C10[12] and the image sensor It is possible to read and write both the SN9C10[12] and the image sensor
registers by using the "sysfs" filesystem interface. registers by using the "sysfs" filesystem interface.
Every time a supported device is recognized, read-only files named "redblue" Every time a supported device is recognized, a read-only file named "green" is
and "green" are created in the /sys/class/video4linux/videoX directory. You can created in the /sys/class/video4linux/videoX directory. You can set the green
set the red, blue and green channel's gain by writing the desired value to channel's gain by writing the desired value to it. The value may range from 0
them. The value may range from 0 to 15 for each channel; this means that to 15.
"redblue" accepts 8-bit values, where the low 4 bits are reserved for red and
the others for blue.
There are other four entries in the directory above for each registered camera: There are other four entries in the directory above for each registered camera:
"reg", "val", "i2c_reg" and "i2c_val". The first two files control the "reg", "val", "i2c_reg" and "i2c_val". The first two files control the
...@@ -217,6 +215,7 @@ Kernel messages will always tell you whether this is the case: ...@@ -217,6 +215,7 @@ Kernel messages will always tell you whether this is the case:
Model Manufacturer Model Manufacturer
----- ------------ ----- ------------
PAS106B PixArt Imaging Inc. PAS106B PixArt Imaging Inc.
PAS202BCB PixArt Imaging Inc.
TAS5110C1B Taiwan Advanced Sensor Corporation TAS5110C1B Taiwan Advanced Sensor Corporation
TAS5130D1B Taiwan Advanced Sensor Corporation TAS5130D1B Taiwan Advanced Sensor Corporation
...@@ -239,8 +238,8 @@ have created for this purpose, which is present in "sn9c102_sensor.h" ...@@ -239,8 +238,8 @@ have created for this purpose, which is present in "sn9c102_sensor.h"
(documentation is included there). As an example, have a look at the code in (documentation is included there). As an example, have a look at the code in
"sn9c102_pas106b.c", which uses the mentioned interface. "sn9c102_pas106b.c", which uses the mentioned interface.
At the moment, not yet supported image sensors are: PAS202B (VGA), At the moment, not yet supported image sensors are: HV7131[D|E1] (VGA),
HV7131[D|E1] (VGA), MI03 (VGA), OV7620 (VGA). MI03 (VGA), OV7620 (VGA).
10. Note for V4L2 developers 10. Note for V4L2 developers
...@@ -273,4 +272,6 @@ I would thank the following persons: ...@@ -273,4 +272,6 @@ I would thank the following persons:
- Stefano Mozzi, who donated 45 EU; - Stefano Mozzi, who donated 45 EU;
- Luca Capello for the donation of a webcam; - Luca Capello for the donation of a webcam;
- Mizuno Takafumi for the donation of a webcam. - Mizuno Takafumi for the donation of a webcam;
- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB
image sensor.
...@@ -163,11 +163,11 @@ config USB_SE401 ...@@ -163,11 +163,11 @@ config USB_SE401
module will be called se401. module will be called se401.
config USB_SN9C102 config USB_SN9C102
tristate "USB SN9C10[12] PC Camera Controller support (EXPERIMENTAL)" tristate "USB SN9C10[12] PC Camera Controller support"
depends on USB && VIDEO_DEV && EXPERIMENTAL depends on USB && VIDEO_DEV
---help--- ---help---
Say Y here if you want support for cameras based on SN9C101 and Say Y here if you want support for cameras based on SONiX SN9C101
SN9C102 PC Camera Controllers. or SN9C102 PC Camera Controllers.
See <file:Documentation/usb/sn9c102.txt> for more informations. See <file:Documentation/usb/sn9c102.txt> for more informations.
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <asm/semaphore.h>
#include "sn9c102_sensor.h" #include "sn9c102_sensor.h"
...@@ -51,8 +52,8 @@ ...@@ -51,8 +52,8 @@
#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL" #define SN9C102_MODULE_LICENSE "GPL"
#define SN9C102_MODULE_VERSION "1:1.01-beta" #define SN9C102_MODULE_VERSION "1:1.06"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) #define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 6)
SN9C102_ID_TABLE; SN9C102_ID_TABLE;
SN9C102_SENSOR_TABLE; SN9C102_SENSOR_TABLE;
......
...@@ -83,7 +83,7 @@ MODULE_PARM_DESC(debug, ...@@ -83,7 +83,7 @@ MODULE_PARM_DESC(debug,
/*****************************************************************************/ /*****************************************************************************/
typedef char sn9c102_sof_header_t[7]; typedef char sn9c102_sof_header_t[12];
typedef char sn9c102_eof_header_t[4]; typedef char sn9c102_eof_header_t[4];
static sn9c102_sof_header_t sn9c102_sof_header[] = { static sn9c102_sof_header_t sn9c102_sof_header[] = {
...@@ -91,8 +91,6 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = { ...@@ -91,8 +91,6 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = {
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
}; };
/* Number of random bytes that complete the SOF above headers */
#define SN9C102_SOFLEN 5
static sn9c102_eof_header_t sn9c102_eof_header[] = { static sn9c102_eof_header_t sn9c102_eof_header[] = {
{0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00},
...@@ -237,9 +235,6 @@ int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) ...@@ -237,9 +235,6 @@ int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
u8* buff = cam->control_buffer; u8* buff = cam->control_buffer;
int res; int res;
if (index == 0x18)
value = (value & 0xcf) | (cam->reg[0x18] & 0x30);
*buff = value; *buff = value;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
...@@ -443,14 +438,15 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) ...@@ -443,14 +438,15 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
static void* sn9c102_find_sof_header(void* mem, size_t len) static void* sn9c102_find_sof_header(void* mem, size_t len)
{ {
size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i; size_t soflen = sizeof(sn9c102_sof_header_t), i;
u8 j, n = sizeof(sn9c102_sof_header) / soflen; u8 j, n = sizeof(sn9c102_sof_header) / soflen;
for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++) for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
for (j = 0; j < n; j++) for (j = 0; j < n; j++)
if (!memcmp(mem + i, sn9c102_sof_header[j], soflen)) /* It's enough to compare 7 bytes */
if (!memcmp(mem + i, sn9c102_sof_header[j], 7))
/* Skips the header */ /* Skips the header */
return mem + i + soflen + SOFLEN; return mem + i + soflen;
return NULL; return NULL;
} }
...@@ -517,10 +513,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -517,10 +513,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
PDBGG("Isochrnous frame: length %u, #%u i", len, i) PDBGG("Isochrnous frame: length %u, #%u i", len, i)
/* NOTE: It is probably correct to assume that SOF and EOF /*
NOTE: It is probably correct to assume that SOF and EOF
headers do not occur between two consecutive packets, headers do not occur between two consecutive packets,
but who knows..Whatever is the truth, this assumption but who knows..Whatever is the truth, this assumption
doesn't introduce bugs. */ doesn't introduce bugs.
*/
redo: redo:
sof = sn9c102_find_sof_header(pos, len); sof = sn9c102_find_sof_header(pos, len);
...@@ -764,9 +762,11 @@ static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) ...@@ -764,9 +762,11 @@ static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
return (u8)val; return (u8)val;
} }
/* NOTE 1: being inside one of the following methods implies that the v4l /*
NOTE 1: being inside one of the following methods implies that the v4l
device exists for sure (see kobjects and reference counters) device exists for sure (see kobjects and reference counters)
NOTE 2: buffers are PAGE_SIZE long */ NOTE 2: buffers are PAGE_SIZE long
*/
static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
{ {
...@@ -1018,24 +1018,6 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ...@@ -1018,24 +1018,6 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
} }
static ssize_t
sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len)
{
ssize_t res = 0;
u8 value;
ssize_t count;
value = sn9c102_strtou8(buf, len, &count);
if (!count)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
}
static ssize_t static ssize_t
sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
{ {
...@@ -1062,7 +1044,6 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, ...@@ -1062,7 +1044,6 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val); sn9c102_show_i2c_val, sn9c102_store_i2c_val);
static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue);
static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
...@@ -1072,7 +1053,6 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) ...@@ -1072,7 +1053,6 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_reg); video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val); video_device_create_file(v4ldev, &class_device_attr_val);
video_device_create_file(v4ldev, &class_device_attr_redblue);
video_device_create_file(v4ldev, &class_device_attr_green); video_device_create_file(v4ldev, &class_device_attr_green);
if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
...@@ -1118,10 +1098,6 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) ...@@ -1118,10 +1098,6 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
ae_endy = v_size / 2; ae_endy = v_size / 2;
int err = 0; int err = 0;
/* These are a sort of stroboscopic signal for some sensors */
err += sn9c102_write_reg(cam, h_size, 0x1a);
err += sn9c102_write_reg(cam, v_size, 0x1b);
err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, v_start, 0x13);
err += sn9c102_write_reg(cam, h_size, 0x15); err += sn9c102_write_reg(cam, h_size, 0x15);
...@@ -1134,8 +1110,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) ...@@ -1134,8 +1110,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
return -EIO; return -EIO;
PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
"%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size, "%u %u %u %u", h_start, v_start, h_size, v_size)
vo_size)
return 0; return 0;
} }
...@@ -1229,7 +1204,10 @@ static int sn9c102_open(struct inode* inode, struct file* filp) ...@@ -1229,7 +1204,10 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
struct sn9c102_device* cam; struct sn9c102_device* cam;
int err = 0; int err = 0;
/* This the only safe way to prevent race conditions with disconnect */ /*
This is the only safe way to prevent race conditions with
disconnect
*/
if (!down_read_trylock(&sn9c102_disconnect)) if (!down_read_trylock(&sn9c102_disconnect))
return -ERESTARTSYS; return -ERESTARTSYS;
...@@ -1727,6 +1705,10 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1727,6 +1705,10 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return -EINVAL; return -EINVAL;
} }
/* Preserve R,G or B origin */
rect->left &= ~1L;
rect->top &= ~1L;
if (rect->width < 16) if (rect->width < 16)
rect->width = 16; rect->width = 16;
if (rect->height < 16) if (rect->height < 16)
...@@ -1747,12 +1729,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1747,12 +1729,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
rect->width &= ~15L; rect->width &= ~15L;
rect->height &= ~15L; rect->height &= ~15L;
{ /* calculate the scaling factor */ { /* calculate the actual scaling factor */
u32 a, b; u32 a, b;
a = rect->width * rect->height; a = rect->width * rect->height;
b = pix_format->width * pix_format->height; b = pix_format->width * pix_format->height;
scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : scale = b ? (u8)((a / b) < 4 ? 1 :
((a / b) > 4 ? 4 : (a / b)))) : 1; ((a / b) < 16 ? 2 : 4)) : 1;
} }
if (cam->stream == STREAM_ON) { if (cam->stream == STREAM_ON) {
...@@ -1879,12 +1861,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1879,12 +1861,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
memcpy(&rect, &(s->_rect), sizeof(rect)); memcpy(&rect, &(s->_rect), sizeof(rect));
{ /* calculate the scaling factor */ { /* calculate the actual scaling factor */
u32 a, b; u32 a, b;
a = rect.width * rect.height; a = rect.width * rect.height;
b = pix->width * pix->height; b = pix->width * pix->height;
scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : scale = b ? (u8)((a / b) < 4 ? 1 :
((a / b) > 4 ? 4 : (a / b)))) : 1; ((a / b) < 16 ? 2 : 4)) : 1;
} }
rect.width = scale * pix->width; rect.width = scale * pix->width;
...@@ -1895,13 +1877,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1895,13 +1877,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (rect.height < 16) if (rect.height < 16)
rect.height = 16; rect.height = 16;
if (rect.width > bounds->left + bounds->width - rect.left) if (rect.width > bounds->left + bounds->width - rect.left)
rect.width = bounds->left+bounds->width - rect.left; rect.width = bounds->left + bounds->width - rect.left;
if (rect.height > bounds->top + bounds->height - rect.top) if (rect.height > bounds->top + bounds->height - rect.top)
rect.height = bounds->top + bounds->height - rect.top; rect.height = bounds->top + bounds->height - rect.top;
rect.width &= ~15L; rect.width &= ~15L;
rect.height &= ~15L; rect.height &= ~15L;
{ /* adjust the scaling factor */
u32 a, b;
a = rect.width * rect.height;
b = pix->width * pix->height;
scale = b ? (u8)((a / b) < 4 ? 1 :
((a / b) < 16 ? 2 : 4)) : 1;
}
pix->width = rect.width / scale; pix->width = rect.width / scale;
pix->height = rect.height / scale; pix->height = rect.height / scale;
......
...@@ -40,13 +40,12 @@ static int pas106b_init(struct sn9c102_device* cam) ...@@ -40,13 +40,12 @@ static int pas106b_init(struct sn9c102_device* cam)
err += sn9c102_i2c_write(cam, 0x02, 0x0c); err += sn9c102_i2c_write(cam, 0x02, 0x0c);
err += sn9c102_i2c_write(cam, 0x03, 0x12); err += sn9c102_i2c_write(cam, 0x03, 0x12);
err += sn9c102_i2c_write(cam, 0x04, 0x05); err += sn9c102_i2c_write(cam, 0x04, 0x05);
err += sn9c102_i2c_write(cam, 0x05, 0x22); err += sn9c102_i2c_write(cam, 0x05, 0x5a);
err += sn9c102_i2c_write(cam, 0x06, 0xac); err += sn9c102_i2c_write(cam, 0x06, 0x88);
err += sn9c102_i2c_write(cam, 0x07, 0x00); err += sn9c102_i2c_write(cam, 0x07, 0x80);
err += sn9c102_i2c_write(cam, 0x08, 0x01); err += sn9c102_i2c_write(cam, 0x08, 0x01);
err += sn9c102_i2c_write(cam, 0x0a, 0x00); err += sn9c102_i2c_write(cam, 0x0a, 0x01);
err += sn9c102_i2c_write(cam, 0x0b, 0x00); err += sn9c102_i2c_write(cam, 0x0b, 0x00);
err += sn9c102_i2c_write(cam, 0x0d, 0x00);
err += sn9c102_i2c_write(cam, 0x10, 0x06); err += sn9c102_i2c_write(cam, 0x10, 0x06);
err += sn9c102_i2c_write(cam, 0x11, 0x06); err += sn9c102_i2c_write(cam, 0x11, 0x06);
err += sn9c102_i2c_write(cam, 0x12, 0x00); err += sn9c102_i2c_write(cam, 0x12, 0x00);
...@@ -64,11 +63,30 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam, ...@@ -64,11 +63,30 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam,
{ {
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_RED_BALANCE: case V4L2_CID_RED_BALANCE:
return (ctrl->value = sn9c102_i2c_read(cam, 0x0c))<0 ? -EIO:0; if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_BLUE_BALANCE: case V4L2_CID_BLUE_BALANCE:
return (ctrl->value = sn9c102_i2c_read(cam, 0x09))<0 ? -EIO:0; if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_GAIN:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_BRIGHTNESS: case V4L2_CID_BRIGHTNESS:
return (ctrl->value = sn9c102_i2c_read(cam, 0x0e))<0 ? -EIO:0; if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_CONTRAST:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
return -EIO;
ctrl->value &= 0x07;
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -87,9 +105,15 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, ...@@ -87,9 +105,15 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
case V4L2_CID_BLUE_BALANCE: case V4L2_CID_BLUE_BALANCE:
err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f); err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
break; break;
case V4L2_CID_BRIGHTNESS: case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
break; break;
case V4L2_CID_BRIGHTNESS:
err += sn9c102_i2c_write(cam, 0x0d, ctrl->value & 0x1f);
break;
case V4L2_CID_CONTRAST:
err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -130,7 +154,7 @@ static struct sn9c102_sensor pas106b = { ...@@ -130,7 +154,7 @@ static struct sn9c102_sensor pas106b = {
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x1f,
.step = 0x01, .step = 0x01,
.default_value = 0x03, .default_value = 0x04,
.flags = 0, .flags = 0,
}, },
{ {
...@@ -140,17 +164,37 @@ static struct sn9c102_sensor pas106b = { ...@@ -140,17 +164,37 @@ static struct sn9c102_sensor pas106b = {
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x1f,
.step = 0x01, .step = 0x01,
.default_value = 0x02, .default_value = 0x06,
.flags = 0,
},
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0x1f,
.step = 0x01,
.default_value = 0x06,
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_BRIGHTNESS, .id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness", .name = "darkness",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x1f,
.step = 0x01, .step = 0x01,
.default_value = 0x06, .default_value = 0x00,
.flags = 0,
},
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "contrast",
.minimum = 0x00,
.maximum = 0x07,
.step = 0x01,
.default_value = 0x00, /* 0x00~0x03 have same effect */
.flags = 0, .flags = 0,
}, },
}, },
...@@ -185,11 +229,13 @@ int sn9c102_probe_pas106b(struct sn9c102_device* cam) ...@@ -185,11 +229,13 @@ int sn9c102_probe_pas106b(struct sn9c102_device* cam)
int r0 = 0, r1 = 0, err = 0; int r0 = 0, r1 = 0, err = 0;
unsigned int pid = 0; unsigned int pid = 0;
/* Minimal initialization to enable the I2C communication /*
NOTE: do NOT change the values! */ Minimal initialization to enable the I2C communication
NOTE: do NOT change the values!
*/
err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 48 MHz */ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
if (err) if (err)
return -EIO; return -EIO;
......
/***************************************************************************
* Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
* <medaglia@undl.org.br> *
* http://cadu.homelinux.com:8080/ *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/delay.h>
#include "sn9c102_sensor.h"
static struct sn9c102_sensor pas202bcb;
static int pas202bcb_init(struct sn9c102_device* cam)
{
int err = 0;
err += sn9c102_write_reg(cam, 0x00, 0x10);
err += sn9c102_write_reg(cam, 0x00, 0x11);
err += sn9c102_write_reg(cam, 0x00, 0x14);
err += sn9c102_write_reg(cam, 0x20, 0x17);
err += sn9c102_write_reg(cam, 0x20, 0x19);
err += sn9c102_write_reg(cam, 0x09, 0x18);
err += sn9c102_i2c_write(cam, 0x02, 0x0c);
err += sn9c102_i2c_write(cam, 0x03, 0x40);
err += sn9c102_i2c_write(cam, 0x04, 0x07);
err += sn9c102_i2c_write(cam, 0x05, 0x25);
err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
err += sn9c102_i2c_write(cam, 0x0e, 0x01);
err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
err += sn9c102_i2c_write(cam, 0x08, 0x01);
err += sn9c102_i2c_write(cam, 0x0b, 0x01);
err += sn9c102_i2c_write(cam, 0x13, 0x63);
err += sn9c102_i2c_write(cam, 0x15, 0x70);
err += sn9c102_i2c_write(cam, 0x11, 0x01);
msleep(400);
return err;
}
static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl)
{
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
return -EIO;
ctrl->value &= 0x0f;
return 0;
case V4L2_CID_BLUE_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0)
return -EIO;
ctrl->value &= 0x0f;
return 0;
case V4L2_CID_GAIN:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_BRIGHTNESS:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x06)) < 0)
return -EIO;
ctrl->value &= 0x0f;
return 0;
default:
return -EINVAL;
}
}
static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
int err = 0;
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f);
break;
case V4L2_CID_BLUE_BALANCE:
err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f);
break;
case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f);
break;
case V4L2_CID_BRIGHTNESS:
err += sn9c102_i2c_write(cam, 0x06, ctrl->value & 0x0f);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x11, 0x01);
return err;
}
static int pas202bcb_set_crop(struct sn9c102_device* cam,
const struct v4l2_rect* rect)
{
struct sn9c102_sensor* s = &pas202bcb;
int err = 0;
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13);
return err;
}
static struct sn9c102_sensor pas202bcb = {
.name = "PAS202BCB",
.maintainer = "Carlos Eduardo Medaglia Dyonisio "
"<medaglia@undl.org.br>",
.frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_2WIRES,
.slave_read_id = 0x40,
.slave_write_id = 0x40,
.init = &pas202bcb_init,
.qctrl = {
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "red balance",
.minimum = 0x00,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x01,
.flags = 0,
},
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "blue balance",
.minimum = 0x00,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x05,
.flags = 0,
},
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0x1f,
.step = 0x01,
.default_value = 0x06,
.flags = 0,
},
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "darkness",
.minimum = 0x00,
.maximum = 0x0f,
.step = 0x01,
.default_value = 0x00,
.flags = 0,
},
},
.get_ctrl = &pas202bcb_get_ctrl,
.set_ctrl = &pas202bcb_set_ctrl,
.cropcap = {
.bounds = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
.defrect = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},
},
.set_crop = &pas202bcb_set_crop,
.pix_format = {
.width = 640,
.height = 480,
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.priv = 8,
}
};
int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
{
int r0 = 0, r1 = 0, err = 0;
unsigned int pid = 0;
/*
* Minimal initialization to enable the I2C communication
* NOTE: do NOT change the values!
*/
err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
if (err)
return -EIO;
r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00);
r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01);
if (r0 < 0 || r1 < 0)
return -EIO;
pid = (r0 << 4) | ((r1 & 0xf0) >> 4);
if (pid != 0x017)
return -ENODEV;
sn9c102_attach_sensor(cam, &pas202bcb);
return 0;
}
This diff is collapsed.
...@@ -27,16 +27,22 @@ static struct sn9c102_sensor tas5110c1b; ...@@ -27,16 +27,22 @@ static struct sn9c102_sensor tas5110c1b;
static int tas5110c1b_init(struct sn9c102_device* cam) static int tas5110c1b_init(struct sn9c102_device* cam)
{ {
const u8 DARKNESS = 0xb7;
int err = 0; int err = 0;
err += sn9c102_write_reg(cam, 0x01, 0x01); err += sn9c102_write_reg(cam, 0x01, 0x01);
err += sn9c102_write_reg(cam, 0x44, 0x01); err += sn9c102_write_reg(cam, 0x44, 0x01);
err += sn9c102_write_reg(cam, 0x00, 0x10); err += sn9c102_write_reg(cam, 0x00, 0x10);
err += sn9c102_write_reg(cam, 0x00, 0x11); err += sn9c102_write_reg(cam, 0x00, 0x11);
err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x0a, 0x14);
err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x60, 0x17);
err += sn9c102_write_reg(cam, 0x06, 0x18); err += sn9c102_write_reg(cam, 0x06, 0x18);
err += sn9c102_write_reg(cam, 0xcb, 0x19); err += sn9c102_write_reg(cam, 0xfb, 0x19);
err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0,
0x80, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x02, 0x20,
DARKNESS, 0, 0);
return err; return err;
} }
...@@ -53,6 +59,11 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam, ...@@ -53,6 +59,11 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam,
err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, v_start, 0x13);
/* Don't change ! */
err += sn9c102_write_reg(cam, 0x14, 0x1a);
err += sn9c102_write_reg(cam, 0x0a, 0x1b);
err += sn9c102_write_reg(cam, 0xfb, 0x19);
return err; return err;
} }
...@@ -60,6 +71,8 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam, ...@@ -60,6 +71,8 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam,
static struct sn9c102_sensor tas5110c1b = { static struct sn9c102_sensor tas5110c1b = {
.name = "TAS5110C1B", .name = "TAS5110C1B",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
.init = &tas5110c1b_init, .init = &tas5110c1b_init,
.cropcap = { .cropcap = {
.bounds = { .bounds = {
...@@ -90,8 +103,9 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) ...@@ -90,8 +103,9 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
/* This sensor has no identifiers, so let's attach it anyway */ /* This sensor has no identifiers, so let's attach it anyway */
sn9c102_attach_sensor(cam, &tas5110c1b); sn9c102_attach_sensor(cam, &tas5110c1b);
/* At the moment, only devices whose PID is 0x6005 have this sensor */ /* At the moment, sensor detection is based on USB pid/vid */
if (tas5110c1b.usbdev->descriptor.idProduct != 0x6005) if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 &&
tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
return -ENODEV; return -ENODEV;
return 0; return 0;
......
...@@ -27,6 +27,7 @@ static struct sn9c102_sensor tas5130d1b; ...@@ -27,6 +27,7 @@ static struct sn9c102_sensor tas5130d1b;
static int tas5130d1b_init(struct sn9c102_device* cam) static int tas5130d1b_init(struct sn9c102_device* cam)
{ {
const u8 DARKNESS = 0xff, CONTRAST = 0xb0, GAIN = 0x08;
int err = 0; int err = 0;
err += sn9c102_write_reg(cam, 0x01, 0x01); err += sn9c102_write_reg(cam, 0x01, 0x01);
...@@ -37,22 +38,15 @@ static int tas5130d1b_init(struct sn9c102_device* cam) ...@@ -37,22 +38,15 @@ static int tas5130d1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x00, 0x14);
err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x60, 0x17);
err += sn9c102_write_reg(cam, 0x07, 0x18); err += sn9c102_write_reg(cam, 0x07, 0x18);
err += sn9c102_write_reg(cam, 0x33, 0x19);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x80,
0x47, 0, 0); 0x00, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
0xa9, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
0x49, 0, 0); GAIN, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
CONTRAST, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
0x6c, 0, 0); DARKNESS, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
0x08, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x20,
0x00, 0, 0);
err += sn9c102_write_reg(cam, 0x63, 0x19);
return err; return err;
} }
...@@ -62,13 +56,18 @@ static int tas5130d1b_set_crop(struct sn9c102_device* cam, ...@@ -62,13 +56,18 @@ static int tas5130d1b_set_crop(struct sn9c102_device* cam,
const struct v4l2_rect* rect) const struct v4l2_rect* rect)
{ {
struct sn9c102_sensor* s = &tas5130d1b; struct sn9c102_sensor* s = &tas5130d1b;
int err = 0;
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
int err = 0;
err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, v_start, 0x13);
/* Do NOT change! */
err += sn9c102_write_reg(cam, 0x1d, 0x1a);
err += sn9c102_write_reg(cam, 0x10, 0x1b);
err += sn9c102_write_reg(cam, 0xf3, 0x19);
return err; return err;
} }
...@@ -108,13 +107,9 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) ...@@ -108,13 +107,9 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
/* This sensor has no identifiers, so let's attach it anyway */ /* This sensor has no identifiers, so let's attach it anyway */
sn9c102_attach_sensor(cam, &tas5130d1b); sn9c102_attach_sensor(cam, &tas5130d1b);
/* At the moment, only devices whose PID is 0x6025 have this sensor */ /* At the moment, sensor detection is based on USB pid/vid */
if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025) if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
return -ENODEV; return -ENODEV;
dev_info(tas5130d1b.dev, "TAS5130D1B detected, but the support for it " return 0;
"is disabled at the moment - needs further "
"testing -\n");
return -ENODEV;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment