4. Module dependencies
5. Module loading
6. Module parameters
-7. Device control through "sysfs"
+7. Optional device control through "sysfs"
8. Supported devices
9. How to add support for new image sensors
-10. Note for V4L2 developers
+10. Notes for V4L2 application developers
11. Contact information
12. Credits
-------------------------------------------------------------------------------
-7. Device control through "sysfs"
-=================================
+7. Optional device control through "sysfs"
+==========================================
It is possible to read and write both the SN9C10[12] and the image sensor
registers by using the "sysfs" filesystem interface.
0xc45 0x6029
0xc45 0x602a
0xc45 0x602c
-0xc45 0x8001
+0xc45 0x6030
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.
MI03 (VGA), OV7620 (VGA).
-10. Note for V4L2 developers
-============================
+10. Notes for V4L2 application developers
+=========================================
This driver follows the V4L2 API specifications. In particular, it enforces two
rules:
-1) Exactly one I/O method, either "mmap" or "read", is associated with each
+- exactly one I/O method, either "mmap" or "read", is associated with each
file descriptor. Once it is selected, the application must close and reopen the
-device to switch to the other I/O method.
-
-2) 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. In case,
-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.
+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.
+
+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
+"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.
11. Contact information
#define SN9C102_DEBUG
#define SN9C102_DEBUG_LEVEL 2
#define SN9C102_MAX_DEVICES 64
+#define SN9C102_PRESERVE_IMGSCALE 0
#define SN9C102_MAX_FRAMES 32
#define SN9C102_URBS 2
#define SN9C102_ISO_PACKETS 7
#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.07"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 7)
+#define SN9C102_MODULE_VERSION "1:1.08"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8)
SN9C102_ID_TABLE;
SN9C102_SENSOR_TABLE;
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/stddef.h>
+#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
rect->width &= ~15L;
rect->height &= ~15L;
- { /* calculate the actual scaling factor */
+ if (SN9C102_PRESERVE_IMGSCALE) {
+ /* Calculate the actual scaling factor */
u32 a, b;
a = rect->width * rect->height;
b = pix_format->width * pix_format->height;
scale = b ? (u8)((a / b) < 4 ? 1 :
((a / b) < 16 ? 2 : 4)) : 1;
- }
+ } else
+ scale = 1;
if (cam->stream == STREAM_ON) {
cam->stream = STREAM_INTERRUPT;
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
frame);
- list_del(&cam->outqueue);
+ list_del(cam->outqueue.next);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
f->state = F_UNUSED;
err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x0d, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f));
break;
case V4L2_CID_CONTRAST:
err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
- .name = "darkness",
+ .name = "brightness",
.minimum = 0x00,
.maximum = 0x1f,
.step = 0x01,
- .default_value = 0x00,
+ .default_value = 0x1f,
.flags = 0,
},
{
err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x06, ctrl->value & 0x0f);
+ err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f));
break;
default:
return -EINVAL;
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
- .name = "darkness",
+ .name = "brightness",
.minimum = 0x00,
.maximum = 0x0f,
.step = 0x01,
- .default_value = 0x00,
+ .default_value = 0x0f,
.flags = 0,
},
},
static int tas5110c1b_init(struct sn9c102_device* cam)
{
- const u8 DARKNESS = 0xb7;
int err = 0;
err += sn9c102_write_reg(cam, 0x01, 0x01);
err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0,
0x80, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x02, 0x20,
- DARKNESS, 0, 0);
return err;
}
+static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ 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);
+ default:
+ return -EINVAL;
+ }
+}
+
+
static int tas5110c1b_set_crop(struct sn9c102_device* cam,
const struct v4l2_rect* rect)
{
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
.init = &tas5110c1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x48,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5110c1b_set_ctrl,
.cropcap = {
.bounds = {
.left = 0,
static int tas5130d1b_init(struct sn9c102_device* cam)
{
- const u8 DARKNESS = 0xff, CONTRAST = 0xb0, GAIN = 0x08;
int err = 0;
err += sn9c102_write_reg(cam, 0x01, 0x01);
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, 0x80,
- 0x00, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
- GAIN, 0, 0);
err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
- CONTRAST, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
- DARKNESS, 0, 0);
+ 0x47, 0, 0);
return err;
}
+static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ 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);
+ default:
+ return -EINVAL;
+ }
+}
+
+
static int tas5130d1b_set_crop(struct sn9c102_device* cam,
const struct v4l2_rect* rect)
{
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
.init = &tas5130d1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5130d1b_set_ctrl,
.cropcap = {
.bounds = {
.left = 0,