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

[PATCH] USB: SN9C10x driver updates

SN9C10x driver updates.

The most important change is the addition of the "SN9C10x" vendor-specific
pixel format, which is used by the applications or libraries to get "raw"
compressed data from the SN9C10x controllers. This also means that V4L2 should
be smart enough for the decompression to be handled in userland. You can
consider this patch as a further response to the discussion we had on the lkml
about the submission of the PWC driver including decompression in kernelspace,
which is present in the Alan Cox's tree.

Changes: (+ new, - removed, * cleanup, @ bugfix)

@ Fix VIDIOC_TRY_FMT ioctl
@ Fix SOF/EOF problems with TAS5130D1B image sensor
@ Fix VIDIOC_ENUM_FMT ioctl
* Replace wait_event_interruptible() with wait_event_timeout()
* Use msecs_to_jiffies() for timeouts in jiffies
* Use usb_make_path() on VIDIOC_QUERYCAP for device path in the usb tree
* read() returns buf.bytesused instead of buf.length on success
- Remove brightness control from PAS106B and PAS202BCB, since it has no effect
  on the active pixel area (it's just for window border lines)
+ Implement VIDIOC_G_PARM and VIDIOC_S_PARM ioctl's
+ Add DAC magnitude, DAC sign, green balance and exposure controls for PAS106B
  and PAS202BCB image sensors
+ Add an additional pixel format for compressed video: V4L2_PIX_FMT_SN9C10X
+ VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP are used to set or get the current
  compression quality
+ New documentation note in "Notes for V4L2 application developers" paragraph
  about video formats and compressed data.
+ Documentation updates about the new features
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 5748930a
...@@ -9,28 +9,32 @@ ...@@ -9,28 +9,32 @@
Index Index
===== =====
1. Copyright 1. Copyright
2. License 2. Disclaimer
3. Overview 3. License
4. Module dependencies 4. Overview
5. Module loading 5. Driver installation
6. Module parameters 6. Module loading
7. Optional device control through "sysfs" 7. Module parameters
8. Supported devices 8. Optional device control through "sysfs"
9. How to add support for new image sensors 9. Supported devices
10. Notes for V4L2 application developers 10. How to add support for new image sensors
11. Contact information 11. Notes for V4L2 application developers
12. Credits 12. Contact information
13. Credits
1. Copyright 1. Copyright
============ ============
Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>
2. Disclaimer
=============
SONiX is a trademark of SONiX Technology Company Limited, inc. SONiX is a trademark of SONiX Technology Company Limited, inc.
This driver is not sponsored or developed by SONiX. This software is not sponsored or developed by SONiX.
2. License 3. License
========== ==========
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -47,29 +51,52 @@ along with this program; if not, write to the Free Software ...@@ -47,29 +51,52 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3. Overview 4. Overview
=========== ===========
This driver attempts to support the video and audio streaming capabilities of 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 the devices mounting the SONiX SN9C101, SN9C102 and SN9C103 (or SUI-102) PC
Camera Controllers. Camera Controllers.
- It's worth to note that SONiX has never collaborated with me during the It's worth to note that SONiX has never collaborated with the author during the
development of this project, despite 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 specifications of the register tables, compression engine and video data format
of the above chips - 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 hotplugging.
The driver relies on the Video4Linux2 and USB core modules. It has been The driver relies on the Video4Linux2 and USB core modules. It has been
designed to run properly on SMP systems as well. designed to run properly on SMP systems as well.
The latest version of the SN9C10x 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/ http://www.linux-projects.org/
Some of the features of the driver are:
4. Module dependencies
- full compliance with the Video4Linux2 API (see also "Notes for V4L2
application developers" paragraph);
- available mmap or read/poll methods for video streaming through isochronous
data transfers;
- automatic detection of image sensor;
- support for any window resolutions and optional panning within the maximum
pixel area of image sensor;
- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both
directions (see "Notes for V4L2 application developers" paragraph);
- two different video formats for uncompressed or compressed data (see also
"Notes for V4L2 application developers" paragraph);
- full support for the capabilities of many of the possible image sensors that
can be connected to the SN9C10x bridges, including, for istance, red, green,
blue and global gain adjustments and exposure (see "Supported devices"
paragraph for details);
- use of default color settings for sunlight conditions;
- dynamic I/O interface for both SN9C10x and image sensor control (see
"Optional device control through 'sysfs'" paragraph);
- dynamic driver control thanks to various module parameters (see "Module
parameters" paragraph);
- 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 hotplugging;
- no known bugs.
5. Module dependencies
====================== ======================
For it to work properly, the driver needs kernel support for Video4Linux and For it to work properly, the driver needs kernel support for Video4Linux and
USB. USB.
...@@ -101,7 +128,7 @@ And finally: ...@@ -101,7 +128,7 @@ And finally:
CONFIG_USB_SN9C102=m CONFIG_USB_SN9C102=m
5. Module loading 6. Module loading
================= =================
To use the driver, it is necessary to load the "sn9c102" module into memory To use the driver, it is necessary to load the "sn9c102" module into memory
after every other module required: "videodev", "usbcore" and, depending on after every other module required: "videodev", "usbcore" and, depending on
...@@ -109,7 +136,6 @@ the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". ...@@ -109,7 +136,6 @@ the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd".
Loading can be done as shown below: Loading can be done as shown below:
[root@localhost home]# modprobe usbcore
[root@localhost home]# modprobe sn9c102 [root@localhost home]# modprobe sn9c102
At this point the devices should be recognized. You can invoke "dmesg" to At this point the devices should be recognized. You can invoke "dmesg" to
...@@ -118,7 +144,7 @@ analyze kernel messages and verify that the loading process has gone well: ...@@ -118,7 +144,7 @@ analyze kernel messages and verify that the loading process has gone well:
[user@localhost home]$ dmesg [user@localhost home]$ dmesg
6. Module parameters 7. Module parameters
==================== ====================
Module parameters are listed below: Module parameters are listed below:
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -144,12 +170,14 @@ Description: Debugging information level, from 0 to 3: ...@@ -144,12 +170,14 @@ Description: Debugging information level, from 0 to 3:
2 = significant informations 2 = significant informations
3 = more verbose messages 3 = more verbose messages
Level 3 is useful for testing only, when only one device Level 3 is useful for testing only, when only one device
is used. is used. It also shows some more informations about the
hardware being detected. This parameter can be changed at
runtime thanks to the /sys filesystem.
Default: 2 Default: 2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
7. Optional device control through "sysfs" 8. Optional device control through "sysfs"
========================================== ==========================================
It is possible to read and write both the SN9C10x 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. registers by using the "sysfs" filesystem interface.
...@@ -191,10 +219,10 @@ Note that the SN9C10x always returns 0 when some of its registers are read. ...@@ -191,10 +219,10 @@ 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. To avoid race conditions, all the I/O accesses to the files are serialized.
8. Supported devices 9. Supported devices
==================== ====================
- I won't mention any of the names of the companies as well as their products None of the names of the companies as well as their products will be mentioned
here. They have never collaborated with me, so no advertising - here. They have never collaborated with the author, so no advertising.
From the point of view of a driver, what unambiguously identify a device are 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 its vendor and product USB identifiers. Below is a list of known identifiers of
...@@ -241,7 +269,7 @@ Vendor ID Product ID ...@@ -241,7 +269,7 @@ Vendor ID Product ID
0x0c45 0x60bc 0x0c45 0x60bc
0x0c45 0x60be 0x0c45 0x60be
The list above does NOT imply that all those devices work with this driver: up 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; until now only the ones that mount the following image sensors are supported;
kernel messages will always tell you whether this is the case: kernel messages will always tell you whether this is the case:
...@@ -259,16 +287,16 @@ If you think your camera is based on the above hardware and is not actually ...@@ -259,16 +287,16 @@ If you think your camera is based on the above hardware and is not actually
listed in the above table, you may try to add the specific USB VendorID and 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"; 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. 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 If this works, please send an email to the author reporting the kernel
that I can add a new entry in the list of supported devices. messages, so that a new entry in the list of supported devices can be added.
Donations of new models for further testing and support would be much 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 appreciated. Non-available hardware won't be supported by the author of this
have. driver.
9. How to add support for new image sensors 10. How to add support for new image sensors
=========================================== ============================================
It should be easy to write code for new sensors by using the small API that I It should be easy to write code for new sensors by using the small API that I
have created for this purpose, which is present in "sn9c102_sensor.h" 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
...@@ -278,7 +306,7 @@ At the moment, possible unsupported image sensors are: HV7131x series (VGA), ...@@ -278,7 +306,7 @@ At the moment, possible unsupported image sensors are: HV7131x series (VGA),
MI03x series (VGA), OV7620 (VGA), OV7630 (VGA), CIS-VF10 (VGA). MI03x series (VGA), OV7620 (VGA), OV7630 (VGA), CIS-VF10 (VGA).
10. Notes for V4L2 application developers 11. Notes for V4L2 application developers
========================================= =========================================
This driver follows the V4L2 API specifications. In particular, it enforces two This driver follows the V4L2 API specifications. In particular, it enforces two
rules: rules:
...@@ -301,8 +329,18 @@ factor can be chosen arbitrarily by the "negotiation" of the "source" and ...@@ -301,8 +329,18 @@ factor can be chosen arbitrarily by the "negotiation" of the "source" and
that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the
scaling factor is restored to 1. scaling factor is restored to 1.
This driver supports two different video formats: the first one is the "8-bit
Sequential Bayer" format and can be used to obtain uncompressed video data
from the device through the current I/O method, while the second one provides
"raw" compressed video data (without the initial and final frame headers). The
compression quality may vary from 0 to 1 and can be selected or queried thanks
to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum
flexibility, the default active video format depends on how the image sensor
being used is initialized (as described in the documentation of the API for the
image sensors supplied by this driver).
11. Contact information 12. Contact information
======================= =======================
I may be contacted by e-mail at <luca.risolia@studio.unibo.it>. I may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
...@@ -311,7 +349,7 @@ My public 1024-bit key should be available at any keyserver; the fingerprint ...@@ -311,7 +349,7 @@ My public 1024-bit key should be available at any keyserver; the fingerprint
is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
12. Credits 13. Credits
=========== ===========
I would thank the following persons: I would thank the following persons:
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/param.h> #include <linux/param.h>
...@@ -45,7 +46,8 @@ ...@@ -45,7 +46,8 @@
#define SN9C102_URBS 2 #define SN9C102_URBS 2
#define SN9C102_ISO_PACKETS 7 #define SN9C102_ISO_PACKETS 7
#define SN9C102_ALTERNATE_SETTING 8 #define SN9C102_ALTERNATE_SETTING 8
#define SN9C102_CTRL_TIMEOUT 10*HZ #define SN9C102_URB_TIMEOUT msecs_to_jiffies(3)
#define SN9C102_CTRL_TIMEOUT msecs_to_jiffies(100)
/*****************************************************************************/ /*****************************************************************************/
...@@ -53,8 +55,8 @@ ...@@ -53,8 +55,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.12" #define SN9C102_MODULE_VERSION "1:1.19"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 12) #define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 19)
enum sn9c102_bridge { enum sn9c102_bridge {
BRIDGE_SN9C101 = 0x01, BRIDGE_SN9C101 = 0x01,
...@@ -100,7 +102,7 @@ enum sn9c102_stream_state { ...@@ -100,7 +102,7 @@ enum sn9c102_stream_state {
}; };
struct sn9c102_sysfs_attr { struct sn9c102_sysfs_attr {
u8 reg, val, i2c_reg, i2c_val; u8 reg, i2c_reg;
}; };
static DECLARE_MUTEX(sn9c102_sysfs_lock); static DECLARE_MUTEX(sn9c102_sysfs_lock);
...@@ -121,11 +123,13 @@ struct sn9c102_device { ...@@ -121,11 +123,13 @@ struct sn9c102_device {
struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
struct list_head inqueue, outqueue; struct list_head inqueue, outqueue;
u32 frame_count, nbuffers; u32 frame_count, nbuffers, nreadbuffers;
enum sn9c102_io_method io; enum sn9c102_io_method io;
enum sn9c102_stream_state stream; enum sn9c102_stream_state stream;
struct v4l2_jpegcompression compression;
struct sn9c102_sysfs_attr sysfs; struct sn9c102_sysfs_attr sysfs;
u16 reg[32]; u16 reg[32];
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/time.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/compiler.h> #include <linux/compiler.h>
...@@ -100,6 +99,7 @@ static sn9c102_eof_header_t sn9c102_eof_header[] = { ...@@ -100,6 +99,7 @@ static sn9c102_eof_header_t sn9c102_eof_header[] = {
}; };
/*****************************************************************************/ /*****************************************************************************/
static void* rvmalloc(size_t size) static void* rvmalloc(size_t size)
{ {
void* mem; void* mem;
...@@ -424,7 +424,8 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) ...@@ -424,7 +424,8 @@ 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(struct sn9c102_device* cam, void* mem, size_t len)
{ {
size_t soflen = sizeof(sn9c102_sof_header_t), 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;
...@@ -440,11 +441,15 @@ static void* sn9c102_find_sof_header(void* mem, size_t len) ...@@ -440,11 +441,15 @@ static void* sn9c102_find_sof_header(void* mem, size_t len)
} }
static void* sn9c102_find_eof_header(void* mem, size_t len) static void*
sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
{ {
size_t eoflen = sizeof(sn9c102_eof_header_t), i; size_t eoflen = sizeof(sn9c102_eof_header_t), i;
unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
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++) for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
for (j = 0; j < n; j++) for (j = 0; j < n; j++)
if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
...@@ -509,9 +514,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -509,9 +514,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
*/ */
redo: redo:
sof = sn9c102_find_sof_header(pos, len); sof = sn9c102_find_sof_header(cam, pos, len);
if (!sof) { if (!sof) {
eof = sn9c102_find_eof_header(pos, len); eof = sn9c102_find_eof_header(cam, pos, len);
if ((*f)->state == F_GRABBING) { if ((*f)->state == F_GRABBING) {
end_of_frame: end_of_frame:
img = len; img = len;
...@@ -539,7 +544,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -539,7 +544,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
(*f)->buf.bytesused += img; (*f)->buf.bytesused += img;
if ((*f)->buf.bytesused == (*f)->buf.length) { if ((*f)->buf.bytesused == (*f)->buf.length ||
(cam->sensor->pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X && eof)) {
u32 b = (*f)->buf.bytesused; u32 b = (*f)->buf.bytesused;
(*f)->state = F_DONE; (*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count; (*f)->buf.sequence= ++cam->frame_count;
...@@ -592,14 +599,20 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) ...@@ -592,14 +599,20 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
goto redo; goto redo;
} else if ((*f)->state == F_GRABBING) { } else if ((*f)->state == F_GRABBING) {
eof = sn9c102_find_eof_header(pos, len); eof = sn9c102_find_eof_header(cam, pos, len);
if (eof && eof < sof) if (eof && eof < sof)
goto end_of_frame; /* (1) */ goto end_of_frame; /* (1) */
else { else {
DBG(3, "SOF before expected EOF after %lu " if (cam->sensor->pix_format.pixelformat ==
"bytes of image data", V4L2_PIX_FMT_SN9C10X) {
(unsigned long)((*f)->buf.bytesused)) eof = sof-sizeof(sn9c102_sof_header_t);
goto start_of_frame; goto end_of_frame;
} else {
DBG(3, "SOF before expected EOF after "
"%lu bytes of image data",
(unsigned long)((*f)->buf.bytesused))
goto start_of_frame;
}
} }
} }
} }
...@@ -723,6 +736,29 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) ...@@ -723,6 +736,29 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
return err; return err;
} }
int sn9c102_stream_interrupt(struct sn9c102_device* cam)
{
int err = 0;
cam->stream = STREAM_INTERRUPT;
err = wait_event_timeout(cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED),
SN9C102_URB_TIMEOUT);
if (err) {
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;
}
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
return 0;
}
/*****************************************************************************/ /*****************************************************************************/
static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
...@@ -1127,6 +1163,35 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) ...@@ -1127,6 +1163,35 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
/*****************************************************************************/ /*****************************************************************************/
static int
sn9c102_set_format(struct sn9c102_device* cam, struct v4l2_pix_format* fmt)
{
int err = 0;
if (fmt->pixelformat == V4L2_PIX_FMT_SN9C10X)
err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18);
else
err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18);
return err ? -EIO : 0;
}
static int
sn9c102_set_compression(struct sn9c102_device* cam,
struct v4l2_jpegcompression* compression)
{
int err = 0;
if (compression->quality == 0)
err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17);
else if (compression->quality == 1)
err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17);
return err ? -EIO : 0;
}
static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
{ {
u8 r = 0; u8 r = 0;
...@@ -1204,6 +1269,20 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1204,6 +1269,20 @@ static int sn9c102_init(struct sn9c102_device* cam)
} }
} }
if (!(cam->state & DEV_INITIALIZED))
cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1;
else
err += sn9c102_set_compression(cam, &cam->compression);
err += sn9c102_set_format(cam, &s->pix_format);
if (err)
return err;
if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
DBG(3, "Compressed video format is active, quality %d",
cam->compression.quality)
else
DBG(3, "Uncompressed video format is active")
if (s->set_crop) if (s->set_crop)
if ((err = s->set_crop(cam, rect))) { if ((err = s->set_crop(cam, rect))) {
DBG(3, "set_crop() failed") DBG(3, "set_crop() failed")
...@@ -1219,9 +1298,12 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1219,9 +1298,12 @@ static int sn9c102_init(struct sn9c102_device* cam)
ctrl.value = qctrl[i].default_value; ctrl.value = qctrl[i].default_value;
err = s->set_ctrl(cam, &ctrl); err = s->set_ctrl(cam, &ctrl);
if (err) { if (err) {
DBG(3, "Set control failed") DBG(3, "Set %s control failed",
s->qctrl[i].name)
return err; return err;
} }
DBG(3, "Image sensor supports '%s' control",
s->qctrl[i].name)
} }
} }
...@@ -1230,6 +1312,7 @@ static int sn9c102_init(struct sn9c102_device* cam) ...@@ -1230,6 +1312,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
spin_lock_init(&cam->queue_lock); spin_lock_init(&cam->queue_lock);
init_waitqueue_head(&cam->wait_frame); init_waitqueue_head(&cam->wait_frame);
init_waitqueue_head(&cam->wait_stream); init_waitqueue_head(&cam->wait_stream);
cam->nreadbuffers = 2;
memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
memcpy(&(s->_rect), &(s->cropcap.defrect), memcpy(&(s->_rect), &(s->cropcap.defrect),
sizeof(struct v4l2_rect)); sizeof(struct v4l2_rect));
...@@ -1387,7 +1470,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1387,7 +1470,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam, 2)) { if (!sn9c102_request_buffers(cam, cam->nreadbuffers)) {
DBG(1, "read() failed, not enough memory") DBG(1, "read() failed, not enough memory")
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENOMEM; return -ENOMEM;
...@@ -1431,8 +1514,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1431,8 +1514,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
sn9c102_queue_unusedframes(cam); sn9c102_queue_unusedframes(cam);
if (count > f->buf.length) if (count > f->buf.bytesused)
count = f->buf.length; count = f->buf.bytesused;
if (copy_to_user(buf, f->bufmem, count)) { if (copy_to_user(buf, f->bufmem, count)) {
up(&cam->fileop_sem); up(&cam->fileop_sem);
...@@ -1553,11 +1636,15 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1553,11 +1636,15 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
return -EINVAL; return -EINVAL;
} }
/* VM_IO is eventually going to replace PageReserved altogether */
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
pos = (unsigned long)cam->frame[i].bufmem; pos = (unsigned long)cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */ while (size > 0) { /* size is page-aligned */
page = vmalloc_to_pfn((void *)pos); page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, if (remap_pfn_range(vma, start, page, PAGE_SIZE,
vma->vm_page_prot)) { vma->vm_page_prot)) {
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -EAGAIN; return -EAGAIN;
} }
...@@ -1567,8 +1654,6 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1567,8 +1654,6 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
} }
vma->vm_ops = &sn9c102_vm_ops; vma->vm_ops = &sn9c102_vm_ops;
vma->vm_flags &= ~VM_IO; /* not I/O memory */
vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
vma->vm_private_data = &cam->frame[i]; vma->vm_private_data = &cam->frame[i];
sn9c102_vm_open(vma); sn9c102_vm_open(vma);
...@@ -1593,11 +1678,14 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1593,11 +1678,14 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
.version = SN9C102_MODULE_VERSION_CODE, .version = SN9C102_MODULE_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | .capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING, V4L2_CAP_STREAMING,
}; };
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info)); if (usb_make_path(cam->usbdev, cap.bus_info,
sizeof(cap.bus_info)) < 0)
strlcpy(cap.bus_info, cam->dev.bus_id,
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap))) if (copy_to_user(arg, &cap, sizeof(cap)))
return -EFAULT; return -EFAULT;
...@@ -1707,6 +1795,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1707,6 +1795,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
s->_qctrl[i].default_value = ctrl.value; s->_qctrl[i].default_value = ctrl.value;
PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
(unsigned long)ctrl.id, (unsigned long)ctrl.value)
return 0; return 0;
} }
...@@ -1803,22 +1894,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1803,22 +1894,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
} else } else
scale = 1; scale = 1;
if (cam->stream == STREAM_ON) { if (cam->stream == STREAM_ON)
cam->stream = STREAM_INTERRUPT; if ((err = sn9c102_stream_interrupt(cam)))
err = wait_event_interruptible
( cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED) );
if (err) {
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; return err;
}
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
}
if (copy_to_user(arg, &crop, sizeof(crop))) { if (copy_to_user(arg, &crop, sizeof(crop))) {
cam->stream = stream; cam->stream = stream;
...@@ -1859,20 +1937,23 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1859,20 +1937,23 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
case VIDIOC_ENUM_FMT: case VIDIOC_ENUM_FMT:
{ {
struct sn9c102_sensor* s = cam->sensor;
struct v4l2_fmtdesc fmtd; struct v4l2_fmtdesc fmtd;
if (copy_from_user(&fmtd, arg, sizeof(fmtd))) if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
return -EFAULT; return -EFAULT;
if (fmtd.index != 0) if (fmtd.index == 0) {
strcpy(fmtd.description, "bayer rgb");
fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
} else if (fmtd.index == 1) {
strcpy(fmtd.description, "compressed");
fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
} else
return -EINVAL; return -EINVAL;
memset(&fmtd, 0, sizeof(fmtd));
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
strcpy(fmtd.description, "bayer rgb"); memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
fmtd.pixelformat = s->pix_format.pixelformat;
if (copy_to_user(arg, &fmtd, sizeof(fmtd))) if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
return -EFAULT; return -EFAULT;
...@@ -1891,8 +1972,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1891,8 +1972,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL; return -EINVAL;
pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8; pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
pfmt->sizeimage = pfmt->height * pfmt->bytesperline; ? 0 : (pfmt->width * pfmt->priv) / 8;
pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
pfmt->field = V4L2_FIELD_NONE; pfmt->field = V4L2_FIELD_NONE;
memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
...@@ -1961,15 +2043,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1961,15 +2043,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
pix->width = rect.width / scale; pix->width = rect.width / scale;
pix->height = rect.height / scale; pix->height = rect.height / scale;
pix->pixelformat = pfmt->pixelformat; if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
pix->pixelformat = pfmt->pixelformat;
pix->priv = pfmt->priv; /* bpp */ pix->priv = pfmt->priv; /* bpp */
pix->colorspace = pfmt->colorspace; pix->colorspace = pfmt->colorspace;
pix->bytesperline = (pix->width * pix->priv) / 8; pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
pix->sizeimage = pix->height * pix->bytesperline; ? 0 : (pix->width * pix->priv) / 8;
pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
pix->field = V4L2_FIELD_NONE; pix->field = V4L2_FIELD_NONE;
if (cmd == VIDIOC_TRY_FMT) if (cmd == VIDIOC_TRY_FMT) {
if (copy_to_user(arg, &format, sizeof(format)))
return -EFAULT;
return 0; return 0;
}
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
...@@ -1978,22 +2066,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1978,22 +2066,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return -EINVAL; return -EINVAL;
} }
if (cam->stream == STREAM_ON) { if (cam->stream == STREAM_ON)
cam->stream = STREAM_INTERRUPT; if ((err = sn9c102_stream_interrupt(cam)))
err = wait_event_interruptible
( cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED) );
if (err) {
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; return err;
}
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
}
if (copy_to_user(arg, &format, sizeof(format))) { if (copy_to_user(arg, &format, sizeof(format))) {
cam->stream = stream; cam->stream = stream;
...@@ -2002,7 +2077,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2002,7 +2077,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
sn9c102_release_buffers(cam); sn9c102_release_buffers(cam);
err = sn9c102_set_crop(cam, &rect); err += sn9c102_set_format(cam, pix);
err += sn9c102_set_crop(cam, &rect);
if (s->set_crop) if (s->set_crop)
err += s->set_crop(cam, &rect); err += s->set_crop(cam, &rect);
err += sn9c102_set_scale(cam, scale); err += sn9c102_set_scale(cam, scale);
...@@ -2031,6 +2107,47 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2031,6 +2107,47 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return 0; return 0;
} }
case VIDIOC_G_JPEGCOMP:
{
if (copy_to_user(arg, &cam->compression,
sizeof(cam->compression)))
return -EFAULT;
return 0;
}
case VIDIOC_S_JPEGCOMP:
{
struct v4l2_jpegcompression jc;
const enum sn9c102_stream_state stream = cam->stream;
int err = 0;
if (copy_from_user(&jc, arg, sizeof(jc)))
return -EFAULT;
if (jc.quality != 0 && jc.quality != 1)
return -EINVAL;
if (cam->stream == STREAM_ON)
if ((err = sn9c102_stream_interrupt(cam)))
return err;
err += sn9c102_set_compression(cam, &jc);
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
return -EIO;
}
cam->compression.quality = jc.quality;
cam->stream = stream;
return 0;
}
case VIDIOC_REQBUFS: case VIDIOC_REQBUFS:
{ {
struct v4l2_requestbuffers rb; struct v4l2_requestbuffers rb;
...@@ -2057,22 +2174,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2057,22 +2174,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return -EINVAL; return -EINVAL;
} }
if (cam->stream == STREAM_ON) { if (cam->stream == STREAM_ON)
cam->stream = STREAM_INTERRUPT; if ((err = sn9c102_stream_interrupt(cam)))
err = wait_event_interruptible
( cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED) );
if (err) {
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; return err;
}
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
}
sn9c102_empty_framequeues(cam); sn9c102_empty_framequeues(cam);
...@@ -2222,22 +2326,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2222,22 +2326,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL; return -EINVAL;
if (cam->stream == STREAM_ON) { if (cam->stream == STREAM_ON)
cam->stream = STREAM_INTERRUPT; if ((err = sn9c102_stream_interrupt(cam)))
err = wait_event_interruptible
( cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED) );
if (err) {
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; return err;
}
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
}
sn9c102_empty_framequeues(cam); sn9c102_empty_framequeues(cam);
...@@ -2246,13 +2337,56 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2246,13 +2337,56 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return 0; return 0;
} }
case VIDIOC_G_PARM:
{
struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp)))
return -EFAULT;
if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
sp.parm.capture.extendedmode = 0;
sp.parm.capture.readbuffers = cam->nreadbuffers;
if (copy_to_user(arg, &sp, sizeof(sp)))
return -EFAULT;
return 0;
}
case VIDIOC_S_PARM:
{
struct v4l2_streamparm sp;
if (copy_from_user(&sp, arg, sizeof(sp)))
return -EFAULT;
if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
sp.parm.capture.extendedmode = 0;
if (sp.parm.capture.readbuffers == 0)
sp.parm.capture.readbuffers = cam->nreadbuffers;
if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
if (copy_to_user(arg, &sp, sizeof(sp)))
return -EFAULT;
cam->nreadbuffers = sp.parm.capture.readbuffers;
return 0;
}
case VIDIOC_G_STD: case VIDIOC_G_STD:
case VIDIOC_S_STD: case VIDIOC_S_STD:
case VIDIOC_QUERYSTD: case VIDIOC_QUERYSTD:
case VIDIOC_ENUMSTD: case VIDIOC_ENUMSTD:
case VIDIOC_QUERYMENU: case VIDIOC_QUERYMENU:
case VIDIOC_G_PARM:
case VIDIOC_S_PARM:
return -EINVAL; return -EINVAL;
default: default:
......
/*************************************************************************** /***************************************************************************
* Driver for PAS106B image sensor connected to the SN9C10x PC Camera * * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
...@@ -38,14 +38,9 @@ static int pas106b_init(struct sn9c102_device* cam) ...@@ -38,14 +38,9 @@ static int pas106b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x09, 0x18); err += sn9c102_write_reg(cam, 0x09, 0x18);
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, 0x04, 0x05);
err += sn9c102_i2c_write(cam, 0x05, 0x5a); err += sn9c102_i2c_write(cam, 0x05, 0x5a);
err += sn9c102_i2c_write(cam, 0x06, 0x88); err += sn9c102_i2c_write(cam, 0x06, 0x88);
err += sn9c102_i2c_write(cam, 0x07, 0x80); err += sn9c102_i2c_write(cam, 0x07, 0x80);
err += sn9c102_i2c_write(cam, 0x08, 0x01);
err += sn9c102_i2c_write(cam, 0x0a, 0x01);
err += sn9c102_i2c_write(cam, 0x0b, 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);
...@@ -62,6 +57,15 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam, ...@@ -62,6 +57,15 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl) struct v4l2_control* ctrl)
{ {
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
{
int r1 = sn9c102_i2c_read(cam, 0x03),
r2 = sn9c102_i2c_read(cam, 0x04);
if (r1 < 0 || r2 < 0)
return -EIO;
ctrl->value = (r1 << 4) | (r2 & 0x0f);
}
return 0;
case V4L2_CID_RED_BALANCE: case V4L2_CID_RED_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
return -EIO; return -EIO;
...@@ -77,16 +81,26 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam, ...@@ -77,16 +81,26 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam,
return -EIO; return -EIO;
ctrl->value &= 0x1f; ctrl->value &= 0x1f;
return 0; return 0;
case V4L2_CID_BRIGHTNESS:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
return -EIO;
ctrl->value &= 0x1f;
return 0;
case V4L2_CID_CONTRAST: case V4L2_CID_CONTRAST:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
return -EIO; return -EIO;
ctrl->value &= 0x07; ctrl->value &= 0x07;
return 0; return 0;
case SN9C102_V4L2_CID_GREEN_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0)
return -EIO;
ctrl->value = (ctrl->value & 0x1f) << 1;
return 0;
case SN9C102_V4L2_CID_DAC_MAGNITUDE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
return -EIO;
ctrl->value &= 0xf8;
return 0;
case SN9C102_V4L2_CID_DAC_SIGN:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0)
return -EIO;
ctrl->value &= 0x01;
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -99,6 +113,10 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, ...@@ -99,6 +113,10 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
int err = 0; int err = 0;
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4);
err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f);
break;
case V4L2_CID_RED_BALANCE: case V4L2_CID_RED_BALANCE:
err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
break; break;
...@@ -108,12 +126,23 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, ...@@ -108,12 +126,23 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
case V4L2_CID_GAIN: case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
break; break;
case V4L2_CID_BRIGHTNESS:
err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value);
break;
case V4L2_CID_CONTRAST: case V4L2_CID_CONTRAST:
err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
break; break;
case SN9C102_V4L2_CID_GREEN_BALANCE:
err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1);
err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1);
break;
case SN9C102_V4L2_CID_DAC_MAGNITUDE:
err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3);
break;
case SN9C102_V4L2_CID_DAC_SIGN:
{
int r;
err += (r = sn9c102_i2c_read(cam, 0x07)) < 0 ? r : 0;
err += sn9c102_i2c_write(cam, 0x07, r | ctrl->value);
}
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -147,6 +176,36 @@ static struct sn9c102_sensor pas106b = { ...@@ -147,6 +176,36 @@ static struct sn9c102_sensor pas106b = {
.slave_write_id = 0x40, .slave_write_id = 0x40,
.init = &pas106b_init, .init = &pas106b_init,
.qctrl = { .qctrl = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x125,
.maximum = 0xfff,
.step = 0x01,
.default_value = 0x140,
.flags = 0,
},
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
.maximum = 0x1f,
.step = 0x01,
.default_value = 0x0d,
.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,
},
{ {
.id = V4L2_CID_RED_BALANCE, .id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
...@@ -168,33 +227,33 @@ static struct sn9c102_sensor pas106b = { ...@@ -168,33 +227,33 @@ static struct sn9c102_sensor pas106b = {
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_GAIN, .id = SN9C102_V4L2_CID_GREEN_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain", .name = "green balance",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x3e,
.step = 0x01, .step = 0x02,
.default_value = 0x0d, .default_value = 0x02,
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_BRIGHTNESS, .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness", .name = "DAC magnitude",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x1f,
.step = 0x01, .step = 0x01,
.default_value = 0x1f, .default_value = 0x01,
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_CONTRAST, .id = SN9C102_V4L2_CID_DAC_SIGN,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "contrast", .name = "DAC sign",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x07, .maximum = 0x01,
.step = 0x01, .step = 0x01,
.default_value = 0x00, /* 0x00~0x03 have same effect */ .default_value = 0x00,
.flags = 0, .flags = 0,
}, },
}, },
......
/*************************************************************************** /***************************************************************************
* Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera * * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
* <medaglia@undl.org.br> * * <medaglia@undl.org.br> *
* http://cadu.homelinux.com:8080/ * * http://cadu.homelinux.com:8080/ *
* * * *
* DAC Magnitude, DAC sign, exposure and green gain controls added by *
* Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
...@@ -41,14 +44,10 @@ static int pas202bcb_init(struct sn9c102_device* cam) ...@@ -41,14 +44,10 @@ static int pas202bcb_init(struct sn9c102_device* cam)
err += sn9c102_i2c_write(cam, 0x02, 0x14); err += sn9c102_i2c_write(cam, 0x02, 0x14);
err += sn9c102_i2c_write(cam, 0x03, 0x40); 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, 0x0d, 0x2c);
err += sn9c102_i2c_write(cam, 0x0e, 0x01); err += sn9c102_i2c_write(cam, 0x0e, 0x01);
err += sn9c102_i2c_write(cam, 0x0f, 0xa9); err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
err += sn9c102_i2c_write(cam, 0x10, 0x08); err += sn9c102_i2c_write(cam, 0x10, 0x08);
err += sn9c102_i2c_write(cam, 0x0b, 0x01);
err += sn9c102_i2c_write(cam, 0x0c, 0x04);
err += sn9c102_i2c_write(cam, 0x13, 0x63); err += sn9c102_i2c_write(cam, 0x13, 0x63);
err += sn9c102_i2c_write(cam, 0x15, 0x70); err += sn9c102_i2c_write(cam, 0x15, 0x70);
err += sn9c102_i2c_write(cam, 0x11, 0x01); err += sn9c102_i2c_write(cam, 0x11, 0x01);
...@@ -63,6 +62,15 @@ static int pas202bcb_get_ctrl(struct sn9c102_device* cam, ...@@ -63,6 +62,15 @@ static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl) struct v4l2_control* ctrl)
{ {
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
{
int r1 = sn9c102_i2c_read(cam, 0x04),
r2 = sn9c102_i2c_read(cam, 0x05);
if (r1 < 0 || r2 < 0)
return -EIO;
ctrl->value = (r1 << 6) | (r2 & 0x3f);
}
return 0;
case V4L2_CID_RED_BALANCE: case V4L2_CID_RED_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
return -EIO; return -EIO;
...@@ -78,11 +86,20 @@ static int pas202bcb_get_ctrl(struct sn9c102_device* cam, ...@@ -78,11 +86,20 @@ static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
return -EIO; return -EIO;
ctrl->value &= 0x1f; ctrl->value &= 0x1f;
return 0; return 0;
case V4L2_CID_BRIGHTNESS: case SN9C102_V4L2_CID_GREEN_BALANCE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x06)) < 0) if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
return -EIO; return -EIO;
ctrl->value &= 0x0f; ctrl->value &= 0x0f;
return 0; return 0;
case SN9C102_V4L2_CID_DAC_MAGNITUDE:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
return -EIO;
return 0;
case SN9C102_V4L2_CID_DAC_SIGN:
if ((ctrl->value = sn9c102_i2c_read(cam, 0x0b)) < 0)
return -EIO;
ctrl->value &= 0x01;
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -95,6 +112,10 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, ...@@ -95,6 +112,10 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
int err = 0; int err = 0;
switch (ctrl->id) { 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: case V4L2_CID_RED_BALANCE:
err += sn9c102_i2c_write(cam, 0x09, ctrl->value); err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break; break;
...@@ -104,8 +125,18 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, ...@@ -104,8 +125,18 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
case V4L2_CID_GAIN: case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x10, ctrl->value); err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
break; break;
case V4L2_CID_BRIGHTNESS: case SN9C102_V4L2_CID_GREEN_BALANCE:
err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value); err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
break;
case SN9C102_V4L2_CID_DAC_MAGNITUDE:
err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
break;
case SN9C102_V4L2_CID_DAC_SIGN:
{
int r;
err += (r = sn9c102_i2c_read(cam, 0x0b)) < 0 ? r : 0;
err += sn9c102_i2c_write(cam, 0x0b, r | ctrl->value);
}
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -141,6 +172,26 @@ static struct sn9c102_sensor pas202bcb = { ...@@ -141,6 +172,26 @@ static struct sn9c102_sensor pas202bcb = {
.slave_write_id = 0x40, .slave_write_id = 0x40,
.init = &pas202bcb_init, .init = &pas202bcb_init,
.qctrl = { .qctrl = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = 0x01e5,
.maximum = 0x3fff,
.step = 0x01,
.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, .id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
...@@ -162,23 +213,33 @@ static struct sn9c102_sensor pas202bcb = { ...@@ -162,23 +213,33 @@ static struct sn9c102_sensor pas202bcb = {
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_GAIN, .id = SN9C102_V4L2_CID_GREEN_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain", .name = "green balance",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x1f, .maximum = 0x0f,
.step = 0x01, .step = 0x01,
.default_value = 0x0c, .default_value = 0x00,
.flags = 0, .flags = 0,
}, },
{ {
.id = V4L2_CID_BRIGHTNESS, .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
.type = V4L2_CTRL_TYPE_INTEGER, .type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness", .name = "DAC magnitude",
.minimum = 0x00, .minimum = 0x00,
.maximum = 0x0f, .maximum = 0xff,
.step = 0x01, .step = 0x01,
.default_value = 0x0f, .default_value = 0x04,
.flags = 0,
},
{
.id = SN9C102_V4L2_CID_DAC_SIGN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "DAC sign",
.minimum = 0x00,
.maximum = 0x01,
.step = 0x01,
.default_value = 0x01,
.flags = 0, .flags = 0,
}, },
}, },
......
...@@ -98,7 +98,7 @@ static const struct usb_device_id sn9c102_id_table[] = { \ ...@@ -98,7 +98,7 @@ static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \
{ USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \ { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \
{ USB_DEVICE(0x0c45, 0x602b), }, \ { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \
{ USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \ { USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \
{ USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
{ USB_DEVICE(0x0c45, 0x6080), }, \ { USB_DEVICE(0x0c45, 0x6080), }, \
...@@ -292,21 +292,25 @@ struct sn9c102_sensor { ...@@ -292,21 +292,25 @@ struct sn9c102_sensor {
NOTE: in case, you must program the SN9C10X chip to get rid of NOTE: in case, you must program the SN9C10X chip to get rid of
blank pixels or blank lines at the _start_ of each line or blank pixels or blank lines at the _start_ of each line or
frame after each HSYNC or VSYNC, so that the image starts with frame after each HSYNC or VSYNC, so that the image starts with
real RGB data (see regs 0x12,0x13) (having set H_SIZE and, real RGB data (see regs 0x12, 0x13) (having set H_SIZE and,
V_SIZE you don't have to care about blank pixels or blank V_SIZE you don't have to care about blank pixels or blank
lines at the end of each line or frame). lines at the end of each line or frame).
*/ */
struct v4l2_pix_format pix_format; struct v4l2_pix_format pix_format;
/* /*
What you have to define here are: initial 'width' and 'height' of What you have to define here are: 1) initial 'width' and 'height' of
the target rectangle, the bayer 'pixelformat' and 'priv' which we'll the target rectangle 2) the initial 'pixelformat', which can be
be used to indicate the number of bits per pixel, 8 or 9. either V4L2_PIX_FMT_SN9C10X (for compressed video) or
Nothing more. V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the
number of bits per pixel for uncompressed video, 8 or 9 (despite the
current value of 'pixelformat').
NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4
of cropcap.defrect.width and cropcap.defrect.height. I of cropcap.defrect.width and cropcap.defrect.height. I
suggest 1/1. suggest 1/1.
NOTE 2: as said above, you have to program the SN9C10X chip to get NOTE 2: The initial compression quality is defined by the first bit
of reg 0x17 during the initialization of the image sensor.
NOTE 3: as said above, you have to program the SN9C10X chip to get
rid of any blank pixels, so that the output of the sensor rid of any blank pixels, so that the output of the sensor
matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR).
*/ */
...@@ -333,4 +337,11 @@ struct sn9c102_sensor { ...@@ -333,4 +337,11 @@ struct sn9c102_sensor {
struct v4l2_rect _rect; struct v4l2_rect _rect;
}; };
/*****************************************************************************/
/* Private ioctl's for control settings supported by some image sensors */
#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE
#define SN9C102_V4L2_CID_DAC_SIGN V4L2_CID_PRIVATE_BASE + 1
#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 2
#endif /* _SN9C102_SENSOR_H_ */ #endif /* _SN9C102_SENSOR_H_ */
/*************************************************************************** /***************************************************************************
* Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
...@@ -150,7 +150,7 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) ...@@ -150,7 +150,7 @@ 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, sensor detection is based on USB pid/vid */ /* Sensor detection is based on USB pid/vid */
if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 && if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 &&
tas5110c1b.usbdev->descriptor.idProduct != 0x6005 && tas5110c1b.usbdev->descriptor.idProduct != 0x6005 &&
tas5110c1b.usbdev->descriptor.idProduct != 0x60ab) tas5110c1b.usbdev->descriptor.idProduct != 0x60ab)
......
/*************************************************************************** /***************************************************************************
* Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
...@@ -96,8 +96,8 @@ static int tas5130d1b_set_crop(struct sn9c102_device* cam, ...@@ -96,8 +96,8 @@ static int tas5130d1b_set_crop(struct sn9c102_device* cam,
err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, v_start, 0x13);
/* Do NOT change! */ /* Do NOT change! */
err += sn9c102_write_reg(cam, 0x1d, 0x1a); err += sn9c102_write_reg(cam, 0x1f, 0x1a);
err += sn9c102_write_reg(cam, 0x10, 0x1b); err += sn9c102_write_reg(cam, 0x1a, 0x1b);
err += sn9c102_write_reg(cam, 0xf3, 0x19); err += sn9c102_write_reg(cam, 0xf3, 0x19);
return err; return err;
...@@ -165,7 +165,7 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) ...@@ -165,7 +165,7 @@ 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, sensor detection is based on USB pid/vid */ /* 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) tas5130d1b.usbdev->descriptor.idProduct != 0x60aa)
return -ENODEV; return -ENODEV;
......
...@@ -219,6 +219,7 @@ struct v4l2_pix_format ...@@ -219,6 +219,7 @@ struct v4l2_pix_format
/* Vendor-specific formats */ /* Vendor-specific formats */
#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compress */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compress */
#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */
/* /*
* F O R M A T E N U M E R A T I O N * F O R M A T E N U M E R A T I O N
......
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