SN9C10x driver updates.
Changes: + new, - removed, * cleanup, @ bugfix
@ Fix stream_interrupt()
@ Fix vidioc_enum_input() and split vidioc_gs_input()
@ Need usb_get|put_dev() when disconnecting, if the device is open
* Use wait_event_interruptible_timeout() instead of wait_event_interruptible()
when waiting for video frames
* replace wake_up_interruptible(&wait_stream) with wake_up(&wait_stream)
* Cleanups and updates in the documentation
+ Use per-device sensor structures
+ Add support for PAS202BCA image sensors
+ Add frame_timeout module parameter
Signed-off-by: Luca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1 = force memory unmapping (save memory)
Default: 0
-------------------------------------------------------------------------------
+Name: frame_timeout
+Type: uint array (min = 0, max = 64)
+Syntax: <n[,...]>
+Description: Timeout for a video frame in seconds. This parameter is
+ specific for each detected camera. This parameter can be
+ changed at runtime thanks to the /sys filesystem interface.
+Default: 2
+-------------------------------------------------------------------------------
Name: debug
Type: ushort
Syntax: <n>
--------- ----------
0x0c45 0x6001
0x0c45 0x6005
+0x0c45 0x6007
0x0c45 0x6009
0x0c45 0x600d
0x0c45 0x6024
MI-0343 Micron Technology, Inc.
OV7630 OmniVision Technologies, Inc.
PAS106B PixArt Imaging, Inc.
+PAS202BCA PixArt Imaging, Inc.
PAS202BCB PixArt Imaging, Inc.
TAS5110C1B Taiwan Advanced Sensor Corporation
TAS5130D1B Taiwan Advanced Sensor Corporation
order):
- Luca Capello for the donation of a webcam;
+- Philippe Coval for having helped testing the PAS202BCA image sensor;
- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the
donation of a webcam;
- Jon Hollstrom for the donation of a webcam;
# Makefile for USB Media drivers
#
-sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o
+sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \
+ sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \
+ sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \
+ sn9c102_tas5130d1b.o
et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
zc0301-objs := zc0301_core.o zc0301_pas202bcb.o
#include <linux/param.h>
#include <linux/rwsem.h>
#include <linux/mutex.h>
-#include <asm/semaphore.h>
+#include <linux/string.h>
+#include <linux/stddef.h>
#include "sn9c102_sensor.h"
#define SN9C102_ALTERNATE_SETTING 8
#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS)
#define SN9C102_CTRL_TIMEOUT 300
+#define SN9C102_FRAME_TIMEOUT 2
/*****************************************************************************/
struct sn9c102_module_param {
u8 force_munmap;
+ u16 frame_timeout;
};
static DEFINE_MUTEX(sn9c102_sysfs_lock);
struct video_device* v4ldev;
enum sn9c102_bridge bridge;
- struct sn9c102_sensor* sensor;
+ struct sn9c102_sensor sensor;
struct usb_device* usbdev;
struct urb* urb[SN9C102_URBS];
/*****************************************************************************/
+struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
+
void
sn9c102_attach_sensor(struct sn9c102_device* cam,
struct sn9c102_sensor* sensor)
{
- cam->sensor = sensor;
- cam->sensor->usbdev = cam->usbdev;
+ memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor));
}
/*****************************************************************************/
#undef PDBG
#define PDBG(fmt, args...) \
-dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
#undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
-#include <linux/stddef.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.26"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 26)
+#define SN9C102_MODULE_VERSION "1:1.27"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27)
/*****************************************************************************/
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
+static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"."
+ "\n");
+
#ifdef SN9C102_DEBUG
static unsigned short debug = SN9C102_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
enum sn9c102_io_method io)
{
- struct v4l2_pix_format* p = &(cam->sensor->pix_format);
- struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
const size_t imagesize = cam->module_param.force_munmap ||
io == IO_READ ?
(p->width * p->height * p->priv) / 8 :
int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
{
- if (!cam->sensor)
- return -1;
-
- return sn9c102_i2c_try_read(cam, cam->sensor, address);
+ return sn9c102_i2c_try_read(cam, &cam->sensor, address);
}
int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
{
- if (!cam->sensor)
- return -1;
-
- return sn9c102_i2c_try_write(cam, cam->sensor, address, value);
+ return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
}
/*****************************************************************************/
size_t eoflen = sizeof(sn9c102_eof_header_t), i;
unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
- if (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
return NULL; /* EOF header does not exist in compressed data */
for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
if ((*f))
(*f)->state = F_QUEUED;
DBG(3, "Stream interrupted");
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
}
if (cam->state & DEV_DISCONNECTED)
(*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
frame);
- imagesize = (cam->sensor->pix_format.width *
- cam->sensor->pix_format.height *
- cam->sensor->pix_format.priv) / 8;
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
soflen = (cam->bridge) == BRIDGE_SN9C103 ?
sizeof(sn9c103_sof_header_t) :
redo:
sof = sn9c102_find_sof_header(cam, pos, len);
- if (!sof) {
+ if (likely(!sof)) {
eof = sn9c102_find_eof_header(cam, pos, len);
if ((*f)->state == F_GRABBING) {
end_of_frame:
img = (eof > pos) ? eof - pos - 1 : 0;
if ((*f)->buf.bytesused+img > imagesize) {
- u32 b = (*f)->buf.bytesused + img -
- imagesize;
+ u32 b;
+ b = (*f)->buf.bytesused + img -
+ imagesize;
img = imagesize - (*f)->buf.bytesused;
DBG(3, "Expected EOF not found: "
"video frame cut");
(*f)->buf.bytesused += img;
if ((*f)->buf.bytesused == imagesize ||
- (cam->sensor->pix_format.pixelformat ==
+ (cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X && eof)) {
- u32 b = (*f)->buf.bytesused;
+ u32 b;
+ b = (*f)->buf.bytesused;
(*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count;
spin_lock(&cam->queue_lock);
if (eof && eof < sof)
goto end_of_frame; /* (1) */
else {
- if (cam->sensor->pix_format.pixelformat ==
+ if (cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X) {
eof = sof - soflen;
goto end_of_frame;
static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
{
- int err = 0;
+ long timeout;
cam->stream = STREAM_INTERRUPT;
- err = wait_event_timeout(cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED),
- SN9C102_URB_TIMEOUT);
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ SN9C102_URB_TIMEOUT);
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- else if (err) {
+ else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use it, close and "
- "open /dev/video%d again.", cam->v4ldev->minor);
- return err;
+ DBG(1, "URB timeout reached. The camera is misconfigured. "
+ "To use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
}
return 0;
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) {
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) {
mutex_unlock(&sn9c102_sysfs_lock);
return -ENOSYS;
}
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) {
mutex_unlock(&sn9c102_sysfs_lock);
return -ENOSYS;
}
video_device_create_file(v4ldev, &class_device_attr_blue);
video_device_create_file(v4ldev, &class_device_attr_red);
}
- if (cam->sensor && cam->sensor->sysfs_ops) {
+ if (cam->sensor.sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
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),
static int sn9c102_init(struct sn9c102_device* cam)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
+ usb_put_dev(cam->usbdev);
+
mutex_unlock(&sn9c102_sysfs_lock);
kfree(cam->control_buffer);
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
struct sn9c102_frame_t* f, * i;
unsigned long lock_flags;
+ long timeout;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
mutex_unlock(&cam->fileop_mutex);
return -EAGAIN;
}
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err) {
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
mutex_unlock(&cam->fileop_mutex);
- return err;
+ return timeout;
}
if (cam->state & DEV_DISCONNECTED) {
mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
- if (cam->state & DEV_MISCONFIGURED) {
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
memset(&i, 0, sizeof(i));
strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
if (copy_to_user(arg, &i, sizeof(i)))
return -EFAULT;
static int
-sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
{
int index;
static int
sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_queryctrl qc;
u8 i;
static int
sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
int err = 0;
u8 i;
static int
sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
u8 i;
int err = 0;
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
static int
sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
{
- struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cc->pixelaspect.numerator = 1;
static int
sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
static int
sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_crop crop;
struct v4l2_rect* rect;
struct v4l2_rect* bounds = &(s->cropcap.bounds);
sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_format format;
- struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_format format;
struct v4l2_pix_format* pix;
struct v4l2_pix_format* pfmt = &(s->pix_format);
struct v4l2_buffer b;
struct sn9c102_frame_t *f;
unsigned long lock_flags;
- int err = 0;
+ long timeout;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
return -EINVAL;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err)
- return err;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- if (cam->state & DEV_MISCONFIGURED)
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
return -EIO;
}
return sn9c102_vidioc_enuminput(cam, arg);
case VIDIOC_G_INPUT:
+ return sn9c102_vidioc_g_input(cam, arg);
+
case VIDIOC_S_INPUT:
- return sn9c102_vidioc_gs_input(cam, arg);
+ return sn9c102_vidioc_s_input(cam, arg);
case VIDIOC_QUERYCTRL:
return sn9c102_vidioc_query_ctrl(cam, arg);
break;
}
- if (!err && cam->sensor) {
- DBG(2, "%s image sensor detected", cam->sensor->name);
+ if (!err) {
+ DBG(2, "%s image sensor detected", cam->sensor.name);
DBG(3, "Support for %s maintained by %s",
- cam->sensor->name, cam->sensor->maintainer);
+ cam->sensor.name, cam->sensor.maintainer);
} else {
DBG(1, "No supported image sensor detected");
err = -ENODEV;
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
sn9c102_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame);
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
} else {
cam->state |= DEV_DISCONNECTED;
sn9c102_release_resources(cam);
err += sn9c102_write_reg(cam, 0x0f, 0x18);
err += sn9c102_write_reg(cam, 0x50, 0x19);
- err += sn9c102_i2c_write(cam, 0x12, 0x8d);
- err += sn9c102_i2c_write(cam, 0x11, 0x00);
+ err += sn9c102_i2c_write(cam, 0x12, 0x80);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
err += sn9c102_i2c_write(cam, 0x15, 0x34);
err += sn9c102_i2c_write(cam, 0x16, 0x03);
err += sn9c102_i2c_write(cam, 0x17, 0x1c);
err += sn9c102_i2c_write(cam, 0x19, 0x06);
err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
err += sn9c102_i2c_write(cam, 0x1b, 0x04);
- err += sn9c102_i2c_write(cam, 0x20, 0x44);
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6);
err += sn9c102_i2c_write(cam, 0x23, 0xee);
err += sn9c102_i2c_write(cam, 0x26, 0xa0);
err += sn9c102_i2c_write(cam, 0x27, 0x9a);
- err += sn9c102_i2c_write(cam, 0x28, 0x20);
+ err += sn9c102_i2c_write(cam, 0x28, 0xa0);
err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2a, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x2b, 0x1f);
err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
err += sn9c102_i2c_write(cam, 0x30, 0x24);
err += sn9c102_i2c_write(cam, 0x32, 0x86);
err += sn9c102_i2c_write(cam, 0x02, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x03, ctrl->value);
+ err += sn9c102_i2c_write(cam, 0x01, ctrl->value);
break;
case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x09);
+ err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78);
break;
case V4L2_CID_AUTOGAIN:
err += sn9c102_i2c_write(cam, 0x13, ctrl->value);
int sn9c102_probe_ov7630(struct sn9c102_device* cam)
{
+ const struct usb_device_id ov7630_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x602c), },
+ { USB_DEVICE(0x0c45, 0x602d), },
+ { USB_DEVICE(0x0c45, 0x608f), },
+ { USB_DEVICE(0x0c45, 0x60b0), },
+ { }
+ };
int err = 0;
- sn9c102_attach_sensor(cam, &ov7630);
-
- if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0)
+ if (!sn9c102_match_id(cam, ov7630_id_table))
return -ENODEV;
err += sn9c102_write_reg(cam, 0x01, 0x01);
err += sn9c102_write_reg(cam, 0x00, 0x01);
err += sn9c102_write_reg(cam, 0x28, 0x17);
-
if (err)
return -EIO;
- err += sn9c102_i2c_write(cam, 0x0b, 0);
+ err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0);
if (err)
return -ENODEV;
+ sn9c102_attach_sensor(cam, &ov7630);
+
return 0;
}
--- /dev/null
+/***************************************************************************
+ * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * 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 pas202bca;
+
+
+static int pas202bca_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, 0x30, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
+ err += sn9c102_i2c_write(cam, 0x03, 0x40);
+ 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, 0x10, 0x08);
+ 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 pas202bca_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x24, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static int pas202bca_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas202bca_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas202bca;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3,
+ 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 pas202bca = {
+ .name = "PAS202BCA",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas202bca_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = 0,
+ },
+ {
+ .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 = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &pas202bca_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &pas202bca_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &pas202bca_set_pix_format
+};
+
+
+int sn9c102_probe_pas202bca(struct sn9c102_device* cam)
+{
+ const struct usb_device_id pas202bca_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x60af), },
+ { }
+ };
+ int err = 0;
+
+ if (!sn9c102_match_id(cam,pas202bca_id_table))
+ return -ENODEV;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas202bca);
+
+ return 0;
+}
int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
-{
+{
int r0 = 0, r1 = 0, err = 0;
unsigned int pid = 0;
extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam);
extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \
&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \
&sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \
&sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \
&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \
&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \
NULL, \
};
+/* Device identification */
+extern struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id);
+
/* Attach a probed sensor to the camera. */
extern void
sn9c102_attach_sensor(struct sn9c102_device* cam,
static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6007), }, \
{ USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x6024), }, \
{ SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \
{ SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \
{ SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \
- { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131x */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \
error code without rolling back.
*/
- const struct usb_device* usbdev;
- /*
- Points to the usb_device struct after the sensor is attached.
- Do not touch unless you know what you are doing.
- */
-
/*
Do NOT write to the data below, it's READ ONLY. It is used by the
core module to store successfully updated values of the above
int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
{
- /* This sensor has no identifiers, so let's attach it anyway */
- sn9c102_attach_sensor(cam, &tas5110c1b);
+ const struct usb_device_id tas5110c1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6001), },
+ { USB_DEVICE(0x0c45, 0x6005), },
+ { USB_DEVICE(0x0c45, 0x60ab), },
+ { }
+ };
/* Sensor detection is based on USB pid/vid */
- if (le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6001 &&
- le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6005 &&
- le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x60ab)
+ if (!sn9c102_match_id(cam, tas5110c1b_id_table))
return -ENODEV;
+ sn9c102_attach_sensor(cam, &tas5110c1b);
+
return 0;
}
int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
{
- /* This sensor has no identifiers, so let's attach it anyway */
- sn9c102_attach_sensor(cam, &tas5130d1b);
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6025), },
+ { USB_DEVICE(0x0c45, 0x60aa), },
+ { }
+ };
/* Sensor detection is based on USB pid/vid */
- if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6025 &&
- le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x60aa)
+ if (!sn9c102_match_id(cam, tas5130d1b_id_table))
return -ENODEV;
+ sn9c102_attach_sensor(cam, &tas5130d1b);
+
return 0;
}