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

[PATCH] USB: SN9C10x driver update

Changes:

- Add support for SN9C103 based devices. The audio capability is already
  supported but not released in this version. I will release
  it once someone donates two SN9C103 based devices.
- Implement VIDIOC_G_CTRL for TAS5110C1B and TAS51130D1B
- Replace "SN9C10[12]" strings with "SN9C10x"
- Add red, green, blue gain controls to the SN9C103
- Memory offsets are now page-aligned
- Fix typos in the documentation
- Documentation updates
- Setting bounds are checked by the core module
- Add exposure control for TAS51130D1B
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 8164fc74
......@@ -2718,7 +2718,7 @@ N: Luca Risolia
E: luca.risolia@studio.unibo.it
P: 1024D/FCE635A4 88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4
D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips
D: V4L2 driver for SN9C10[12] PC Camera Controllers
D: V4L2 driver for SN9C10x PC Camera Controllers
S: Via Liberta' 41/A
S: Osio Sotto, 24046, Bergamo
S: Italy
......
SN9C10[12] PC Camera Controllers
SN9C10x PC Camera Controllers
Driver for Linux
================================
=============================
- Documentation -
......@@ -49,22 +49,23 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3. Overview
===========
This driver attempts to support the video streaming capabilities of the devices
mounting the SONiX SN9C101 or SONiX SN9C102 PC Camera Controllers.
This driver attempts to support the video and audio streaming capabilities of
the devices mounting the SONiX SN9C101, SN9C102 and SN9C103 (or SUI-102) PC
Camera Controllers.
- It's worth to note that SONiX has never collaborated with me during the
development of this project, despite of several requests for enough detailed
development of this project, despite several requests for enough detailed
specifications of the register tables, compression engine and video data format
of the above chips -
Up to 64 cameras can be handled at the same time. They can be connected and
disconnected from the host many times without turning off the computer, if
your system supports the hotplug facility.
your system supports hotplugging.
The driver relies on the Video4Linux2 and USB core modules. It has been
designed to run properly on SMP systems as well.
The latest version of the SN9C10[12] driver can be found at the following URL:
The latest version of the SN9C10x driver can be found at the following URL:
http://go.lamarinapunto.com/
......@@ -150,17 +151,20 @@ Default: 2
7. Optional device control through "sysfs"
==========================================
It is possible to read and write both the SN9C10[12] and the image sensor
It is possible to read and write both the SN9C10x and the image sensor
registers by using the "sysfs" filesystem interface.
Every time a supported device is recognized, a read-only file named "green" is
Every time a supported device is recognized, a write-only file named "green" is
created in the /sys/class/video4linux/videoX directory. You can set the green
channel's gain by writing the desired value to it. The value may range from 0
to 15.
to 15 for SN9C101 or SN9C102 bridges, from 0 to 127 for SN9C103 bridges.
Similarly, only for SN9C103 controllers, blue and red gain control files are
available in the same directory, for which accepted values may range from 0 to
127.
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
SN9C10[12] bridge, while the other two control the sensor chip. "reg" and
SN9C10x bridge, while the other two control the sensor chip. "reg" and
"i2c_reg" hold the values of the current register index where the following
reading/writing operations are addressed at through "val" and "i2c_val". Their
use is not intended for end-users, unless you know what you are doing. Note
......@@ -169,19 +173,19 @@ support the standard I2C protocol. Also, remember that you must be logged in as
root before writing to them.
As an example, suppose we were to want to read the value contained in the
register number 1 of the sensor register table - which usually is the product
register number 1 of the sensor register table - which is usually the product
identifier - of the camera registered as "/dev/video0":
[root@localhost #] cd /sys/class/video4linux/video0
[root@localhost #] echo 1 > i2c_reg
[root@localhost #] cat i2c_val
Now let's set the green gain's register of the SN9C10[12] chip to 2:
Now let's set the green gain's register of the SN9C10x chip to 2:
[root@localhost #] echo 0x11 > reg
[root@localhost #] echo 2 > val
Note that the SN9C10[12] always returns 0 when some of its registers are read.
Note that the SN9C10x always returns 0 when some of its registers are read.
To avoid race conditions, all the I/O accesses to the files are serialized.
......@@ -192,25 +196,52 @@ here. They have never collaborated with me, so no advertising -
From the point of view of a driver, what unambiguously identify a device are
its vendor and product USB identifiers. Below is a list of known identifiers of
devices mounting the SN9C10[12] PC camera controllers:
devices mounting the SN9C10x PC camera controllers:
Vendor ID Product ID
--------- ----------
0xc45 0x6001
0xc45 0x6005
0xc45 0x6009
0xc45 0x600d
0xc45 0x6024
0xc45 0x6025
0xc45 0x6028
0xc45 0x6029
0xc45 0x602a
0xc45 0x602c
0xc45 0x6030
0x0c45 0x6001
0x0c45 0x6005
0x0c45 0x6009
0x0c45 0x600d
0x0c45 0x6024
0x0c45 0x6025
0x0c45 0x6028
0x0c45 0x6029
0x0c45 0x602a
0x0c45 0x602b
0x0c45 0x602c
0x0c45 0x6030
0x0c45 0x6080
0x0c45 0x6082
0x0c45 0x6083
0x0c45 0x6088
0x0c45 0x608a
0x0c45 0x608b
0x0c45 0x608c
0x0c45 0x608e
0x0c45 0x608f
0x0c45 0x60a0
0x0c45 0x60a2
0x0c45 0x60a3
0x0c45 0x60a8
0x0c45 0x60aa
0x0c45 0x60ab
0x0c45 0x60ac
0x0c45 0x60ae
0x0c45 0x60af
0x0c45 0x60b0
0x0c45 0x60b2
0x0c45 0x60b3
0x0c45 0x60b8
0x0c45 0x60ba
0x0c45 0x60bb
0x0c45 0x60bc
0x0c45 0x60be
The list above does NOT imply that all those devices work with this driver: up
until now only the ones that mount the following image sensors are supported.
Kernel messages will always tell you whether this is the case:
until now only the ones that mount the following image sensors are supported;
kernel messages will always tell you whether this is the case:
Model Manufacturer
----- ------------
......@@ -224,7 +255,7 @@ listed in the above table, you may try to add the specific USB VendorID and
ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h";
then compile, load the module again and look at the kernel output.
If this works, please send an email to me reporting the kernel messages, so
that I will add a new entry in the list of supported devices.
that I can add a new entry in the list of supported devices.
Donations of new models for further testing and support would be much
appreciated. I won't add official support for hardware that I don't actually
......@@ -238,8 +269,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
"sn9c102_pas106b.c", which uses the mentioned interface.
At the moment, not yet supported image sensors are: HV7131[D|E1] (VGA),
MI03 (VGA), OV7620 (VGA).
At the moment, possible unsupported image sensors are: HV7131x series (VGA),
MI03x series (VGA), OV7620 (VGA), OV7630 (VGA), CIS-VF10 (VGA).
10. Notes for V4L2 application developers
......@@ -254,12 +285,13 @@ device to switch to the other I/O method;
- previously mapped buffer memory must always be unmapped before calling any
of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. The same
number of buffers as before will be allocated again to match the size of the
new video frames, so you have to map them again before any I/O attempts.
new video frames, so you have to map the buffers again before any I/O attempts
on them.
Consistently with the hardware limits, this driver also supports image
downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions.
However the V4L2 API specifications don't correctly define how the scaling
factor can be choosen arbitrarily by the "negotiation" of the "source" and
However, the V4L2 API specifications don't correctly define how the scaling
factor can be chosen arbitrarily by the "negotiation" of the "source" and
"target" rectangles. To work around this flaw, we have added the convention
that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the
scaling factor is restored to 1.
......
......@@ -2346,7 +2346,7 @@ L: linux-usb-devel@lists.sourceforge.net
W: http://www.connecttech.com
S: Supported
USB SN9C10[12] DRIVER
USB SN9C10x DRIVER
P: Luca Risolia
M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net
......
......@@ -123,11 +123,11 @@ config USB_SE401
module will be called se401.
config USB_SN9C102
tristate "USB SN9C10[12] PC Camera Controller support"
tristate "USB SN9C10x PC Camera Controller support"
depends on USB && VIDEO_DEV
---help---
Say Y here if you want support for cameras based on SONiX SN9C101
or SN9C102 PC Camera Controllers.
Say Y here if you want support for cameras based on SONiX SN9C101,
SN9C102 or SN9C103 PC Camera Controllers.
See <file:Documentation/usb/sn9c102.txt> for more informations.
......
/***************************************************************************
* V4L2 driver for SN9C10[12] PC Camera Controllers *
* V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
......@@ -49,12 +49,18 @@
/*****************************************************************************/
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers"
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
#define SN9C102_MODULE_VERSION "1:1.08"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8)
#define SN9C102_MODULE_VERSION "1:1.10"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 10)
enum sn9c102_bridge {
BRIDGE_SN9C101 = 0x01,
BRIDGE_SN9C102 = 0x02,
BRIDGE_SN9C103 = 0x04,
};
SN9C102_ID_TABLE;
SN9C102_SENSOR_TABLE;
......@@ -105,6 +111,7 @@ struct sn9c102_device {
struct video_device* v4ldev;
enum sn9c102_bridge bridge;
struct sn9c102_sensor* sensor;
struct usb_device* usbdev;
......
/***************************************************************************
* V4L2 driver for SN9C10[12] PC Camera Controllers *
* V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
......@@ -169,15 +169,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
cam->nbuffers = count;
while (cam->nbuffers > 0) {
if ((buff = rvmalloc(cam->nbuffers * imagesize)))
if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
cam->frame[i].bufmem = buff + i*imagesize;
cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
cam->frame[i].buf.m.offset = i*imagesize;
cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.length = imagesize;
cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buf.sequence = 0;
......@@ -388,7 +388,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
data[4] = data3;
data[5] = data4;
data[6] = data5;
data[7] = 0x10;
data[7] = 0x14;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
if (res < 0)
......@@ -1022,15 +1022,79 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
static ssize_t
sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
{
struct sn9c102_device* cam;
enum sn9c102_bridge bridge;
ssize_t res = 0;
u8 value;
ssize_t count;
if (down_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
up(&sn9c102_sysfs_lock);
return -ENODEV;
}
bridge = cam->bridge;
up(&sn9c102_sysfs_lock);
value = sn9c102_strtou8(buf, len, &count);
if (!count || value > 0x0f)
if (!count)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
switch (bridge) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
if (value > 0x0f)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
break;
case BRIDGE_SN9C103:
if (value > 0x7f)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
break;
}
return res;
}
static ssize_t
sn9c102_store_blue(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 || value > 0x7f)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
}
static ssize_t
sn9c102_store_red(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 || value > 0x7f)
return -EINVAL;
if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
......@@ -1046,6 +1110,8 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
static void sn9c102_create_sysfs(struct sn9c102_device* cam)
......@@ -1054,7 +1120,12 @@ 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_val);
video_device_create_file(v4ldev, &class_device_attr_green);
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
video_device_create_file(v4ldev, &class_device_attr_green);
else if (cam->bridge == BRIDGE_SN9C103) {
video_device_create_file(v4ldev, &class_device_attr_blue);
video_device_create_file(v4ldev, &class_device_attr_red);
}
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_val);
......@@ -1092,21 +1163,13 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
v_start = (u8)(rect->top - s->cropcap.bounds.top),
h_size = (u8)(rect->width / 16),
v_size = (u8)(rect->height / 16),
ae_strx = 0x00,
ae_stry = 0x00,
ae_endx = h_size / 2,
ae_endy = v_size / 2;
v_size = (u8)(rect->height / 16);
int err = 0;
err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13);
err += sn9c102_write_reg(cam, h_size, 0x15);
err += sn9c102_write_reg(cam, v_size, 0x16);
err += sn9c102_write_reg(cam, ae_strx, 0x1c);
err += sn9c102_write_reg(cam, ae_stry, 0x1d);
err += sn9c102_write_reg(cam, ae_endx, 0x1e);
err += sn9c102_write_reg(cam, ae_endy, 0x1f);
if (err)
return -EIO;
......@@ -1636,16 +1699,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
if ((err = s->set_ctrl(cam, &ctrl)))
return err;
n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
for (i = 0; i < n; i++)
if (ctrl.id == s->qctrl[i].id) {
s->_qctrl[i].default_value = ctrl.value;
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
ctrl.value -= ctrl.value % s->qctrl[i].step;
break;
}
if ((err = s->set_ctrl(cam, &ctrl)))
return err;
s->_qctrl[i].default_value = ctrl.value;
return 0;
}
......@@ -1776,7 +1844,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_CROP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
return err;
return -EIO;
}
s->pix_format.width = rect->width/scale;
......@@ -1951,7 +2019,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_FMT failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
return err;
return -EIO;
}
memcpy(pfmt, pix, sizeof(*pix));
......@@ -2286,15 +2354,17 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) {
DBG(1, "Sorry, this is not a SN9C10[12] based camera "
DBG(1, "Sorry, this is not a SN9C10x based camera "
"(vid/pid 0x%04X/0x%04X)",
sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
err = -ENODEV;
goto fail;
}
DBG(2, "SN9C10[12] PC Camera Controller detected "
"(vid/pid 0x%04X/0x%04X)",
cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
BRIDGE_SN9C102 : BRIDGE_SN9C103;
DBG(2, "SN9C10x PC Camera Controller detected (vid/pid 0x%04X/0x%04X)",
sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
for (i = 0; sn9c102_sensor_table[i]; i++) {
......@@ -2318,7 +2388,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->state |= DEV_MISCONFIGURED;
}
strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
......
/***************************************************************************
* Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera *
* Driver for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
......@@ -100,26 +100,26 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f));
err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value);
break;
case V4L2_CID_CONTRAST:
err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x13, 0x01);
return err;
return err ? -EIO : 0;
}
......
/***************************************************************************
* Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera *
* Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
......@@ -95,23 +95,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f);
err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f);
err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
break;
case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f);
err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f));
err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x11, 0x01);
return err;
return err ? -EIO : 0;
}
......
/***************************************************************************
* API for image sensors connected to the SN9C10[12] PC Camera Controllers *
* API for image sensors connected to the SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
......@@ -89,17 +89,44 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
#define SN9C102_ID_TABLE \
static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \
{ USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \
{ USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \
{ USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \
{ USB_DEVICE(0xc45, 0x6024), }, \
{ USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
{ USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \
{ USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \
{ USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \
{ USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \
{ USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \
{ USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x6024), }, \
{ USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \
{ USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \
{ USB_DEVICE(0x0c45, 0x602b), }, \
{ USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \
{ USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
{ USB_DEVICE(0x0c45, 0x6080), }, \
{ USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \
{ USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \
{ USB_DEVICE(0x0c45, 0x6088), }, \
{ USB_DEVICE(0x0c45, 0x608a), }, \
{ USB_DEVICE(0x0c45, 0x608b), }, \
{ USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \
{ USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \
{ USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \
{ USB_DEVICE(0x0c45, 0x60a0), }, \
{ USB_DEVICE(0x0c45, 0x60a2), }, \
{ USB_DEVICE(0x0c45, 0x60a3), }, \
{ USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \
{ USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x60ac), }, \
{ USB_DEVICE(0x0c45, 0x60ae), }, \
{ USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \
{ USB_DEVICE(0x0c45, 0x60b0), }, \
{ USB_DEVICE(0x0c45, 0x60b2), }, \
{ USB_DEVICE(0x0c45, 0x60b3), }, \
{ USB_DEVICE(0x0c45, 0x60b8), }, \
{ USB_DEVICE(0x0c45, 0x60ba), }, \
{ USB_DEVICE(0x0c45, 0x60bb), }, \
{ USB_DEVICE(0x0c45, 0x60bc), }, \
{ USB_DEVICE(0x0c45, 0x60be), }, \
{ } \
};
......@@ -173,9 +200,7 @@ struct sn9c102_sensor {
/*
These identifiers must be provided if the image sensor implements
the standard I2C protocol. TASC sensors don't, although they have a
serial interface: so this is a case where the "raw" I2C version
could be helpful.
the standard I2C protocol.
*/
u8 slave_read_id, slave_write_id; /* reg. 0x09 */
......@@ -214,7 +239,8 @@ struct sn9c102_sensor {
the list above. The returned value must follow the V4L2
specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
are not supported by this driver, so do not implement them. Also,
passed values are NOT checked to see if they are out of bounds.
you don't have to check whether the passed values are out of bounds,
given that this is done by the core module.
*/
struct v4l2_cropcap cropcap;
......
/***************************************************************************
* Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC *
* Camera Controllers *
* Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
......@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5110c1b;
static struct v4l2_control tas5110c1b_gain;
static int tas5110c1b_init(struct sn9c102_device* cam)
{
......@@ -38,25 +40,42 @@ static int tas5110c1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x06, 0x18);
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_write(cam, 0xc0, 0x80);
return err;
}
static int tas5110c1b_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl)
{
switch (ctrl->id) {
case V4L2_CID_GAIN:
ctrl->value = tas5110c1b_gain.value;
break;
default:
return -EINVAL;
}
return 0;
}
static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
int err = 0;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11,
0x02, 0x20,
0xff - (ctrl->value & 0xff),
0, 0);
if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
tas5110c1b_gain.value = ctrl->value;
break;
default:
return -EINVAL;
}
return err ? -EIO : 0;
}
......@@ -85,6 +104,8 @@ static struct sn9c102_sensor tas5110c1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
.slave_read_id = 0xff, /* fictitious */
.slave_write_id = 0xff, /* fictitious */
.init = &tas5110c1b_init,
.qctrl = {
{
......@@ -92,9 +113,9 @@ static struct sn9c102_sensor tas5110c1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0xff,
.maximum = 0xf6,
.step = 0x01,
.default_value = 0x48,
.default_value = 0x40,
.flags = 0,
},
},
......@@ -113,6 +134,7 @@ static struct sn9c102_sensor tas5110c1b = {
.height = 288,
},
},
.get_ctrl = &tas5110c1b_get_ctrl,
.set_crop = &tas5110c1b_set_crop,
.pix_format = {
.width = 352,
......@@ -130,7 +152,8 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
/* At the moment, sensor detection is based on USB pid/vid */
if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 &&
tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
tas5110c1b.usbdev->descriptor.idProduct != 0x6005 &&
tas5110c1b.usbdev->descriptor.idProduct != 0x60ab)
return -ENODEV;
return 0;
......
/***************************************************************************
* Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC *
* Camera Controllers *
* Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
......@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5130d1b;
static struct v4l2_control tas5130d1b_gain, tas5130d1b_exposure;
static int tas5130d1b_init(struct sn9c102_device* cam)
{
......@@ -38,25 +40,47 @@ static int tas5130d1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x60, 0x17);
err += sn9c102_write_reg(cam, 0x07, 0x18);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
0x47, 0, 0);
return err;
}
static int tas5130d1b_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl)
{
switch (ctrl->id) {
case V4L2_CID_GAIN:
ctrl->value = tas5130d1b_gain.value;
break;
case V4L2_CID_EXPOSURE:
ctrl->value = tas5130d1b_exposure.value;
break;
default:
return -EINVAL;
}
return 0;
}
static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
int err = 0;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11,
0x02, 0x20,
0xff - (ctrl->value & 0xff),
0, 0);
if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
tas5130d1b_gain.value = ctrl->value;
break;
case V4L2_CID_EXPOSURE:
if (!(err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value)))
tas5130d1b_exposure.value = ctrl->value;
break;
default:
return -EINVAL;
}
return err ? -EIO : 0;
}
......@@ -85,6 +109,8 @@ static struct sn9c102_sensor tas5130d1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
.slave_read_id = 0xff, /* fictitious */
.slave_write_id = 0xff, /* fictitious */
.init = &tas5130d1b_init,
.qctrl = {
{
......@@ -92,12 +118,23 @@ static struct sn9c102_sensor tas5130d1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0xff,
.maximum = 0xf6,
.step = 0x02,
.default_value = 0x00,
.flags = 0,
},
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x00,
.maximum = 0x47,
.step = 0x01,
.default_value = 0x00,
.flags = 0,
},
},
.get_ctrl = &tas5130d1b_get_ctrl,
.set_ctrl = &tas5130d1b_set_ctrl,
.cropcap = {
.bounds = {
......@@ -129,7 +166,8 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
sn9c102_attach_sensor(cam, &tas5130d1b);
/* At the moment, sensor detection is based on USB pid/vid */
if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025 &&
tas5130d1b.usbdev->descriptor.idProduct != 0x60aa)
return -ENODEV;
return 0;
......
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