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

[PATCH] USB: W996[87]CF driver update

This patch contains updates and one bug fix.
parent 8d4f1c82
......@@ -2670,9 +2670,9 @@ S: 70110 Kuopio
S: Finland
N: Luca Risolia
E: luca_ing@libero.it
D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip
S: Via Libertà 41/a
E: luca.risolia@studio.unibo.it
D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips
S: Via Liberta' 41/A
S: Osio Sotto, 24046, Bergamo
S: Italy
......
W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6
==============================================================
W996[87]CF JPEG USB Dual Mode Camera Chip
Driver for Linux 2.6 (basic version)
=========================================
- Documentation -
......@@ -11,15 +12,16 @@ Index
2. License
3. Overview
4. Supported devices
5. Kernel configuration and third-part module compilation
5. Module dependencies
6. Module loading
7. Module paramaters
8. Credits
8. Contact information
9. Credits
1. Copyright
============
Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>
Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
2. License
......@@ -43,23 +45,25 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
===========
This driver supports the video streaming capabilities of the devices mounting
Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they
are being commanded by USB.
are being commanded by USB. OV681 based cameras should be supported as well.
The driver relies on the Video4Linux, USB and I2C core modules of the Linux
kernel, version 2.6.0 or greater, and is not compatible in any way with
previous versions. It has been designed to run properly on SMP systems
as well. At the moment, an additional module, "ovcamchip", is mandatory; it
provides support for some OmniVision CMOS sensors connected to the W996[87]CF
chips.
The driver is split into two modules: the basic one, "w9968cf", is needed for
The driver is divided into two modules: the basic one, "w9968cf", is needed for
the supported devices to work; the second one, "w9968cf-vpp", is an optional
module, which provides some useful video post-processing functions like video
decoding, up-scaling and colour conversions. These routines can't be included
into official kernels for performance purposes. Once the driver is installed,
decoding, up-scaling and colour conversions. Once the driver is installed,
every time an application tries to open a recognized device, "w9968cf" checks
the presence of the "w9968cf-vpp" module and loads it automatically by default.
Please keep in mind that official kernels do NOT include the second module for
performance purposes. However it is always recommended to download and install
the latest and complete release of the driver, replacing the existing one, if
present: it will be still even possible not to load the "w9968cf-vpp" module at
all, if you ever want to.
The latest and full-featured version of the W996[87]CF driver can be found at:
http://go.lamarinapunto.com/ . Please refer to the documentation included in
that package, if you are going to use it.
Up to 32 cameras can be handled at the same time. They can be connected and
disconnected from the host many times without turning off the computer, if
your system supports the hotplug facility.
......@@ -67,18 +71,21 @@ your system supports the hotplug facility.
To change the default settings for each camera, many paramaters can be passed
through command line when the module is loaded into memory.
The latest and full featured version of the W996[87]CF driver can be found at:
http://go.lamarinapunto.com/
The driver relies on the Video4Linux, USB and I2C core modules of the official
Linux kernels. It has been designed to run properly on SMP systems as well.
At the moment, an additional module, "ovcamchip", is mandatory; it provides
support for some OmniVision CMOS sensors connected to the W996[87]CF chips.
The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be
The "ovcamchip" module is part of the OV511 driver, version 2.27, which can be
downloaded from internet:
http://alpha.dyndns.org/ov511/
To know how to patch, compile and load it, read the paragraphs below.
To know how to compile it, read the documentation included in the OV511
package.
4. Supported devices
====================
At the moment, known W996[87]CF based devices are:
At the moment, known W996[87]CF and OV681 based devices are:
- Aroma Digi Pen ADG-5000 Refurbished
- AVerTV USB
- Creative Labs Video Blaster WebCam Go
......@@ -87,27 +94,26 @@ At the moment, known W996[87]CF based devices are:
- Ezonics EZ-802 EZMega Cam
- OPCOM Digi Pen VGA Dual Mode Pen Camera
If you know any other W996[87]CF based cameras, please contact me.
If you know any other W996[87]CF or OV681 based cameras, please contact me.
The list above does NOT imply that all those devices work with this driver: up
until now only webcams that have a CMOS sensor supported by the "ovcamchip"
module work.
For a list of supported CMOS sensors, please visit the module author homepage:
http://alpha.dyndns.org/ov511/
For a list of supported CMOS sensors, please visit the author's homepage on
this module: http://alpha.dyndns.org/ov511/
Possible external microcontrollers of those webcams are not supported: this
means that still images can't be downloaded from the device memory.
means that still images cannot be downloaded from the device memory.
Furthermore, it's worth to note that I was only able to run tests on my
"Creative Labs Video Blaster WebCam Go". Donations of other models, for
additional testing and full support, would be much appreciated.
5. Kernel configuration and third-part module compilation
=========================================================
As noted above, kernel 2.6.0 is the minimum for this driver; for it to work
properly, the driver needs kernel support for Video4Linux, USB and I2C, and a
third-part module for the CMOS sensor.
5. Module dependencies
======================
For it to work properly, the driver needs kernel support for Video4Linux,
USB and I2C, and a third-party module for the CMOS sensor.
The following options of the kernel configuration file must be enabled and
corresponding modules must be compiled:
......@@ -126,7 +132,7 @@ The I2C core module can be compiled statically in the kernel as well.
#
CONFIG_USB=m
In addition, depending on the hardware being used, just one of the modules
In addition, depending on the hardware being used, only one of the modules
below is necessary:
# USB Host Controller Drivers
......@@ -137,31 +143,16 @@ below is necessary:
Also, make sure "Enforce bandwidth allocation" is NOT enabled.
And finally:
# USB Multimedia devices
#
CONFIG_USB_W9968CF=m
The last module we need is "ovcamchip.o". To obtain it, you have to download
the OV511 driver, version 2.25 - don't use other versions - which is available
at http://alpha.dyndns.org/ov511/ . Then you have to download the latest
version of the full featured W996[87]CF driver, which contains a patch for the
"ovcamchip" module; it is available at http://go.lamarinapunto.com .
Once you have obtained the packages, decompress, patch and compile the
"ovcamchip" module. In other words:
[user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz
[user@localhost home]$ tar xvjf ov511-2.25.tar.bz2
[user@localhost home]$ cd ov511-2.25
[user@localhost ov511-2.25]$ patch -p1 < \
/path/to/w9968cf-x.x/ov511-2.25.patch
[user@localhost ov511-2.25]$ make
It's worth to note that the full featured version of the W996[87]CF driver
can also be installed overwriting the one in the kernel; in this case, read the
documentation included in the package.
If everything went well, the W996[87]CF driver can be immediatly used (see next
paragraph).
the OV511 package, version 2.27 - don't use other versions - and compile it
according to its documentation.
The package is available at http://alpha.dyndns.org/ov511/ .
6. Module loading
......@@ -169,7 +160,7 @@ paragraph).
To use the driver, it is necessary to load the "w9968cf" module into memory
after every other module required.
For example, loading can be done this way, as root:
Loading can be done this way, from root:
[root@localhost home]# modprobe usbcore
[root@localhost home]# modprobe i2c-core
......@@ -191,11 +182,10 @@ explanation about them and which syntax to use, it is recommended to run the
7. Module paramaters
====================
Module paramaters are listed below:
-------------------------------------------------------------------------------
Name: vppmod_load
Type: int
Type: bool
Syntax: <0|1>
Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
If enabled, every time an application attempts to open a
......@@ -219,7 +209,7 @@ Syntax: <-1|n[,...]>
Description: Specify V4L minor mode number.
-1 = use next available
n = use minor number n
You can specify 32 cameras this way.
You can specify up to 32 cameras this way.
For example:
video_nr=-1,2,-1 would assign minor number 2 to the second
recognized camera and use auto for the first one and for every
......@@ -236,22 +226,22 @@ Default: 1023
Name: max_buffers
Type: int array (min = 0, max = 32)
Syntax: <n[,...]>
Description: Only for advanced users.
Description: For advanced users.
Specify the maximum number of video frame buffers to allocate
for each device, from 2 to 32.
Default: 2
-------------------------------------------------------------------------------
Name: double_buffer
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Hardware double buffering: 0 disabled, 1 enabled.
It should be enabled if you want smooth video output: if you
obtain out of sync. video, disable it at all, or try to
obtain out of sync. video, disable it, or try to
decrease the 'clockdiv' module paramater value.
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: clamping
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Video data clamping: 0 disabled, 1 enabled.
Default: 0 for every device.
......@@ -266,13 +256,13 @@ Description: Video filter type.
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: largeview
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Large view: 0 disabled, 1 enabled.
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: upscaling
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Software scaling (for non-compressed video only):
0 disabled, 1 enabled.
......@@ -319,7 +309,7 @@ Default: 0 for every device. Initial palette is 9 (UYVY).
Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9.
-------------------------------------------------------------------------------
Name: force_rgb
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Read RGB video data instead of BGR:
1 = use RGB component ordering.
......@@ -328,28 +318,28 @@ Description: Read RGB video data instead of BGR:
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: autobright
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes brightness:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: autoexp
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes exposure:
0 = no, 1 = yes
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: lightfreq
Type: long array (min = 0, max = 32)
Type: int array (min = 0, max = 32)
Syntax: <50|60[,...]>
Description: Light frequency in Hz:
50 for European and Asian lighting, 60 for American lighting.
Default: 50 for every device.
-------------------------------------------------------------------------------
Name: bandingfilter
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Banding filter to reduce effects of fluorescent
lighting:
......@@ -359,7 +349,7 @@ Description: Banding filter to reduce effects of fluorescent
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: clockdiv
Type: long array (min = 0, max = 32)
Type: int array (min = 0, max = 32)
Syntax: <-1|n[,...]>
Description: Force pixel clock divisor to a specific value (for experts):
n may vary from 0 to 127.
......@@ -368,21 +358,21 @@ Description: Force pixel clock divisor to a specific value (for experts):
Default: -1 for every device.
-------------------------------------------------------------------------------
Name: backlight
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Objects are lit from behind:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: mirror
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Reverse image horizontally:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: sensor_mono
Type: long array (min = 0, max = 32)
Name: monochrome
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: The CMOS sensor is monochrome:
0 = no, 1 = yes
......@@ -423,7 +413,7 @@ Name: debug
Type: int
Syntax: <n>
Description: Debugging information level, from 0 to 6:
0 = none (be cautious)
0 = none (use carefully)
1 = critical errors
2 = significant informations
3 = configuration or general messages
......@@ -435,7 +425,7 @@ Description: Debugging information level, from 0 to 6:
Default: 2
-------------------------------------------------------------------------------
Name: specific_debug
Type: int
Type: bool
Syntax: <0|1>
Description: Enable or disable specific debugging messages:
0 = print messages concerning every level <= 'debug' level.
......@@ -444,7 +434,16 @@ Default: 0
-------------------------------------------------------------------------------
8. Credits
8. Contact information
======================
I may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'.
My public 1024-bit key should be available at your keyserver; the fingerprint
is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
9. Credits
==========
The development would not have proceed much further without having looked at
the source code of other drivers and without the help of several persons; in
......@@ -456,8 +455,6 @@ particular:
- memory management code has been copied from the bttv driver by Ralph Metzler,
Marcus Metzler and Gerd Knorr;
- the low-level I2C read function has been written by Frédéric Jouault, who
also gave me commented logs about sniffed USB traffic taken from another
driver for another system;
- the low-level I2C read function has been written by Frederic Jouault;
- the low-level I2C fast write function has been written by Piotr Czerczak;
- the low-level I2C fast write function has been written by Piotr Czerczak.
......@@ -2223,7 +2223,7 @@ S: Maintained
USB W996[87]CF DRIVER
P: Luca Risolia
M: luca_ing@libero.it
M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net
W: http://go.lamarinapunto.com
S: Maintained
......
......@@ -182,24 +182,21 @@ config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support"
depends on USB && VIDEO_DEV && I2C
---help---
Say Y here if you want support for cameras based on
Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
This driver has an optional plugin, which is distributed as a
separate module only (released under GPL). It contains code that
allows you to use higher resolutions and framerates, and can't
be included into the official Linux kernel for performance
purposes.
At the moment the driver needs a third-part module for the CMOS
separate module only (released under GPL). It allows to use higher
resolutions and framerates, but cannot be included in the official
Linux kernel for performance purposes.
At the moment the driver needs a third-party module for the CMOS
sensors, which is available on internet: it is recommended to read
<file:Documentation/usb/w9968cf.txt> for more informations and for
a list of supported cameras.
This driver uses the Video For Linux and the I2C APIs.
You must say Y or M to both "Video For Linux" and
"I2C Support" to use this driver.
Information on this API and pointers to "v4l" programs may be found
on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
This driver uses the Video For Linux and the I2C APIs. You must say
Y or M to both "Video For Linux" and "I2C Support" to use this
driver.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
......
/***************************************************************************
* Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* - Memory management code from bttv driver by Ralph Metzler, *
* Marcus Metzler and Gerd Knorr. *
* - I2C interface to kernel, high-level CMOS sensor control routines and *
* some symbolic names from OV511 driver by Mark W. McClelland. *
* - Low-level I2C fast write function by Piotr Czerczak. *
* - Low-level I2C read function by Frédéric Jouault. *
* - Low-level I2C read function by Frederic Jouault. *
* *
* 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 *
......@@ -27,6 +27,7 @@
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
......@@ -48,81 +49,86 @@
/****************************************************************************
* Modules paramaters *
* Module macros and paramaters *
****************************************************************************/
static u8 vppmod_load = W9968CF_VPPMOD_LOAD;
static u8 simcams = W9968CF_SIMCAMS;
static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */
static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE};
static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS};
static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DOUBLE_BUFFER};
static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE};
static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DECOMPRESSION};
static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ};
static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
W9968CF_BANDINGFILTER};
static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO};
static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS};
static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST};
static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS};
MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL);
MODULE_DESCRIPTION(W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION);
MODULE_LICENSE(W9968CF_MODULE_LICENSE);
MODULE_SUPPORTED_DEVICE("Video");
static int vppmod_load = W9968CF_VPPMOD_LOAD;
static unsigned short simcams = W9968CF_SIMCAMS;
static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_PACKET_SIZE};
static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_BUFFERS};
static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DOUBLE_BUFFER};
static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_FILTER_TYPE};
static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DECOMPRESSION};
static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_LIGHTFREQ};
static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
W9968CF_BANDINGFILTER};
static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME};
static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_BRIGHTNESS};
static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_CONTRAST};
static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_WHITENESS};
#ifdef W9968CF_DEBUG
static u8 debug = W9968CF_DEBUG_LEVEL;
static u8 specific_debug = W9968CF_SPECIFIC_DEBUG;
static unsigned short debug = W9968CF_DEBUG_LEVEL;
static int specific_debug = W9968CF_SPECIFIC_DEBUG;
#endif
MODULE_AUTHOR("Luca Risolia <luca_ing@libero.it>");
MODULE_DESCRIPTION("Video4Linux driver for "
"W996[87]CF JPEG USB Dual Mode Camera Chip");
MODULE_SUPPORTED_DEVICE("Video");
MODULE_LICENSE("GPL");
MODULE_PARM(vppmod_load, "i");
MODULE_PARM(simcams, "i");
MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
static unsigned int param_nv[23]; /* number of values per paramater */
module_param(vppmod_load, bool, 0444);
module_param(simcams, ushort, 0444);
module_param_array(video_nr, short, param_nv[0], 0444);
module_param_array(packet_size, uint, param_nv[1], 0444);
module_param_array(max_buffers, ushort, param_nv[2], 0444);
module_param_array(double_buffer, bool, param_nv[3], 0444);
module_param_array(clamping, bool, param_nv[4], 0444);
module_param_array(filter_type, ushort, param_nv[5], 0444);
module_param_array(largeview, bool, param_nv[6], 0444);
module_param_array(decompression, ushort, param_nv[7], 0444);
module_param_array(upscaling, bool, param_nv[8], 0444);
module_param_array(force_palette, ushort, param_nv[9], 0444);
module_param_array(force_rgb, ushort, param_nv[10], 0444);
module_param_array(autobright, bool, param_nv[11], 0444);
module_param_array(autoexp, bool, param_nv[12], 0444);
module_param_array(lightfreq, ushort, param_nv[13], 0444);
module_param_array(bandingfilter, bool, param_nv[14], 0444);
module_param_array(clockdiv, short, param_nv[15], 0444);
module_param_array(backlight, bool, param_nv[16], 0444);
module_param_array(mirror, bool, param_nv[17], 0444);
module_param_array(monochrome, bool, param_nv[18], 0444);
module_param_array(brightness, uint, param_nv[19], 0444);
module_param_array(hue, uint, param_nv[20], 0444);
module_param_array(colour, uint, param_nv[21], 0444);
module_param_array(contrast, uint, param_nv[22], 0444);
module_param_array(whiteness, uint, param_nv[23], 0444);
#ifdef W9968CF_DEBUG
MODULE_PARM(debug, "i");
MODULE_PARM(specific_debug, "i");
module_param(debug, ushort, 0444);
module_param(specific_debug, bool, 0444);
#endif
MODULE_PARM_DESC(vppmod_load,
......@@ -146,7 +152,7 @@ MODULE_PARM_DESC(video_nr,
"\n<-1|n[,...]> Specify V4L minor mode number."
"\n -1 = use next available (default)"
"\n n = use minor number n (integer >= 0)"
"\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES)
"\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES)
" cameras this way."
"\nFor example:"
"\nvideo_nr=-1,2,-1 would assign minor number 2 to"
......@@ -160,7 +166,7 @@ MODULE_PARM_DESC(packet_size,
"(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
"\n");
MODULE_PARM_DESC(max_buffers,
"\n<n[,...]> Only for advanced users."
"\n<n[,...]> For advanced users."
"\nSpecify the maximum number of video frame buffers"
"\nto allocate for each device, from 2 to "
__MODULE_STRING(W9968CF_MAX_BUFFERS)
......@@ -203,7 +209,8 @@ MODULE_PARM_DESC(upscaling,
"\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
" for every device."
"\nIf 'w9968cf-vpp' is not loaded, this paramater is"
" set to 0.");
" set to 0."
"\n");
MODULE_PARM_DESC(decompression,
"\n<0|1|2[,...]> Software video decompression:"
"\n- 0 disables decompression (doesn't allow formats needing"
......@@ -218,7 +225,8 @@ MODULE_PARM_DESC(decompression,
"\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
" for every device."
"\nIf 'w9968cf-vpp' is not loaded, forcing decompression is "
"\nnot allowed; in this case this paramater is set to 2.");
"\nnot allowed; in this case this paramater is set to 2."
"\n");
MODULE_PARM_DESC(force_palette,
"\n<0"
"|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
......@@ -252,7 +260,8 @@ MODULE_PARM_DESC(force_palette,
"\nInitial palette is "
__MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
"\nIf 'w9968cf-vpp' is not loaded, this paramater is"
" set to 9 (UYVY).");
" set to 9 (UYVY)."
"\n");
MODULE_PARM_DESC(force_rgb,
"\n<0|1[,...]> Read RGB video data instead of BGR:"
"\n 1 = use RGB component ordering."
......@@ -311,10 +320,11 @@ MODULE_PARM_DESC(mirror,
"\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
" for every device."
"\n");
MODULE_PARM_DESC(sensor_mono,
"\n<0|1[,...]> The OV CMOS sensor is monochrome:"
MODULE_PARM_DESC(monochrome,
"\n<0|1[,...]> Use OV CMOS sensor as monochrome sensor:"
"\n 0 = no, 1 = yes"
"\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO)
"\nNot all the sensors support monochrome color."
"\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
" for every device."
"\n");
MODULE_PARM_DESC(brightness,
......@@ -346,7 +356,7 @@ MODULE_PARM_DESC(whiteness,
#ifdef W9968CF_DEBUG
MODULE_PARM_DESC(debug,
"\n<n> Debugging information level, from 0 to 6:"
"\n0 = none (be cautious)"
"\n0 = none (use carefully)"
"\n1 = critical errors"
"\n2 = significant informations"
"\n3 = configuration or general messages"
......@@ -380,13 +390,12 @@ static int w9968cf_open(struct inode*, struct file*);
static int w9968cf_release(struct inode*, struct file*);
static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*);
static int w9968cf_mmap(struct file*, struct vm_area_struct*);
static int w9968cf_ioctl(struct inode*, struct file*,
unsigned int, unsigned long);
static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*);
static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long);
static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void*);
/* USB-specific */
static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
static int w9968cf_start_transfer(struct w9968cf_device*);
static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
static int w9968cf_stop_transfer(struct w9968cf_device*);
static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
......@@ -402,6 +411,7 @@ static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
static int w9968cf_smbus_write_ack(struct w9968cf_device*);
static int w9968cf_smbus_read_ack(struct w9968cf_device*);
static int w9968cf_smbus_refresh_bus(struct w9968cf_device*);
static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
u16 address, u8* value);
static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address,
......@@ -436,12 +446,11 @@ static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
static inline int w9968cf_sensor_cmd(struct w9968cf_device*,
unsigned int cmd, void *arg);
static void w9968cf_sensor_configure(struct w9968cf_device*);
static int w9968cf_sensor_change_settings(struct w9968cf_device*);
static int w9968cf_sensor_get_picture(struct w9968cf_device*,
struct video_picture*);
static int w9968cf_sensor_set_picture(struct w9968cf_device*,
struct video_picture pict);
static int w9968cf_sensor_init(struct w9968cf_device*);
static int w9968cf_sensor_update_settings(struct w9968cf_device*);
static int w9968cf_sensor_get_picture(struct w9968cf_device*);
static int w9968cf_sensor_update_picture(struct w9968cf_device*,
struct video_picture pict);
/* Other helper functions */
static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
......@@ -587,8 +596,6 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
* Memory management functions *
****************************************************************************/
/* Shameless copy from bttv-driver.c */
/* Here we want the physical address of the memory.
This is used when initializing the contents of the area. */
static inline unsigned long kvirt_to_pa(unsigned long adr)
......@@ -639,7 +646,6 @@ static void rvfree(void* mem, unsigned long size)
}
vfree(mem);
}
/* End of shameless copy */
/*--------------------------------------------------------------------------
......@@ -820,9 +826,9 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
}
for (i = 0; i < urb->number_of_packets; i++) {
len = urb->iso_frame_desc[i].actual_length;
len = urb->iso_frame_desc[i].actual_length;
status = urb->iso_frame_desc[i].status;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (status && len != 0) {
DBG(4, "URB failed, error in data packet "
......@@ -923,7 +929,6 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
for (i = 0; i < W9968CF_URBS; i++) {
urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
cam->urb[i] = urb;
if (!urb) {
for (j = 0; j < i; j++)
usb_free_urb(cam->urb[j]);
......@@ -953,7 +958,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
t_size = (w*h*d)/16;
err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
/* Transfer size */
......@@ -1026,11 +1031,12 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam)
spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
for (i = W9968CF_URBS-1; i >= 0; i--)
if (cam->urb[i])
if (cam->urb[i]) {
if (!usb_unlink_urb(cam->urb[i])) {
usb_free_urb(cam->urb[i]);
cam->urb[i] = NULL;
}
}
if (cam->disconnected)
goto exit;
......@@ -1086,8 +1092,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, (void*)buff,
2, W9968CF_USB_CTRL_TIMEOUT);
0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT);
if (res < 0)
DBG(4, "Failed to read a register "
......@@ -1099,7 +1104,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
/*--------------------------------------------------------------------------
Write data to the fast serial bus registers.
Write 64-bit data to the fast serial bus registers.
Return 0 on success, -1 otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
......@@ -1112,8 +1117,7 @@ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, (void*)data, 6,
W9968CF_USB_CTRL_TIMEOUT);
value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT);
if (res < 0)
DBG(4, "Failed to write the FSB registers "
......@@ -1275,6 +1279,22 @@ static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
}
/* This seems to refresh the communication through the serial bus */
static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam)
{
int err = 0, j;
for (j = 1; j <= 10; j++) {
err = w9968cf_write_reg(cam, 0x0020, 0x01);
err += w9968cf_write_reg(cam, 0x0000, 0x01);
if (err)
break;
}
return err;
}
/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
static int
w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
......@@ -1283,8 +1303,10 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
u16* data = cam->data_buffer;
int err = 0;
/* Enable SBUS outputs */
err += w9968cf_write_reg(cam, 0x0020, 0x01);
err += w9968cf_smbus_refresh_bus(cam);
/* Enable SBUS outputs */
err += w9968cf_write_sb(cam, 0x0020);
data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
data[0] |= (address & 0x40) ? 0x4000 : 0x0;
......@@ -1328,7 +1350,7 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
err += w9968cf_write_fsb(cam, data);
/* Disable SBUS outputs */
err += w9968cf_write_reg(cam, 0x0000, 0x01);
err += w9968cf_write_sb(cam, 0x0000);
if (!err)
DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
......@@ -1365,7 +1387,7 @@ w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
err += w9968cf_smbus_read_byte(cam, value);
err += w9968cf_smbus_write_ack(cam);
err += w9968cf_smbus_stop(cam);
/* Serial data disable */
err += w9968cf_write_sb(cam, 0x0000);
......@@ -1434,8 +1456,8 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
int size, union i2c_smbus_data *data)
{
struct w9968cf_device* cam = i2c_get_adapdata(adapter);
u8 i, j;
int rc = 0, err = 0;
u8 i;
int err = 0;
switch (addr) {
case OV6xx0_SID:
......@@ -1451,31 +1473,26 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
addr <<= 1;
if (read_write == I2C_SMBUS_WRITE)
rc = w9968cf_i2c_adap_write_byte(cam, addr, command);
err = w9968cf_i2c_adap_write_byte(cam, addr, command);
else if (read_write == I2C_SMBUS_READ)
rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte);
err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte);
} else if (size == I2C_SMBUS_BYTE_DATA) {
addr <<= 1;
if (read_write == I2C_SMBUS_WRITE)
rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
command, data->byte);
else if (read_write == I2C_SMBUS_READ) {
for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
rc = w9968cf_i2c_adap_read_byte_data(cam, addr,
err = w9968cf_i2c_adap_read_byte_data(cam,addr,
command, &data->byte);
if (rc < 0) {
/* Work around: this seems to wake up
the EEPROM from the stall state */
for (j = 0; j <= 10; j++) {
err += w9968cf_write_sb(cam,0x0020);
err += w9968cf_write_sb(cam,0x0000);
if (err)
break;
if (err) {
if (w9968cf_smbus_refresh_bus(cam)) {
err = -EIO;
break;
}
}
else
} else
break;
}
......@@ -1487,11 +1504,7 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
return -EINVAL;
}
/* This works around a bug in the I2C core */
if (rc > 0)
rc = 0;
return rc;
return err;
}
......@@ -1507,41 +1520,22 @@ static int w9968cf_i2c_attach_inform(struct i2c_client* client)
{
struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
const char* clientname = i2c_clientname(client);
int id = client->driver->id;
int id = client->driver->id, err = 0;
if (id == I2C_DRIVERID_OVCAMCHIP) {
int rc = 0;
cam->sensor_client = client;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
&cam->sensor_mono);
if (rc < 0) {
DBG(1, "CMOS sensor initialization failed (rc=%d)",rc);
cam->sensor_client = NULL;
return rc;
}
if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
&cam->sensor) < 0)
rc = -EIO;
else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID)
w9968cf_sensor_configure(cam);
else
rc = -EINVAL;
if (rc < 0) {
err = w9968cf_sensor_init(cam);
if (err) {
cam->sensor_client = NULL;
cam->sensor = CC_UNKNOWN;
return rc;
return err;
}
} else {
DBG(4, "Rejected client [%s] with [%s]",
} else {
DBG(4, "Rejected client [%s] with driver [%s]",
clientname, client->driver->name)
return -1;
return -EINVAL;
}
DBG(2, "I2C attach client [%s] with [%s]",
DBG(5, "I2C attach client [%s] with driver [%s]",
clientname, client->driver->name)
return 0;
......@@ -1557,7 +1551,7 @@ static int w9968cf_i2c_detach_inform(struct i2c_client* client)
cam->sensor_client = NULL;
}
DBG(2, "I2C detach [%s]", clientname)
DBG(5, "I2C detach client [%s]", clientname)
return 0;
}
......@@ -1573,7 +1567,7 @@ w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
static int w9968cf_i2c_init(struct w9968cf_device* cam)
{
int rc = 0;
int err = 0;
static struct i2c_algorithm algo = {
.name = "W996[87]CF algorithm",
......@@ -1596,15 +1590,15 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam)
strcpy(cam->i2c_adapter.name, "w9968cf");
i2c_set_adapdata(&cam->i2c_adapter, cam);
DBG(6, "Registering I2C bus with kernel...")
DBG(6, "Registering I2C adapter with kernel...")
rc = i2c_add_adapter(&cam->i2c_adapter);
if (rc)
DBG(5, "Failed to register the I2C bus.")
err = i2c_add_adapter(&cam->i2c_adapter);
if (err)
DBG(1, "Failed to register the I2C adapter.")
else
DBG(5, "I2C bus registered.")
DBG(5, "I2C adapter registered.")
return rc;
return err;
}
......@@ -1644,7 +1638,7 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam)
--------------------------------------------------------------------------*/
static int w9968cf_init_chip(struct w9968cf_device* cam)
{
int err = 0, rc = 0;
int err = 0;
err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
......@@ -1679,22 +1673,11 @@ static int w9968cf_init_chip(struct w9968cf_device* cam)
err += w9968cf_set_window(cam, cam->window);
if (err)
goto error;
rc = w9968cf_sensor_change_settings(cam);
if (rc)
goto error;
DBG(5, "Chip successfully initialized.");
return 0;
error:
DBG(1, "Chip initialization failed.")
if (err)
return err;
DBG(1, "Chip initialization failed.")
else
return rc;
DBG(5, "Chip successfully initialized.")
return err;
}
......@@ -1706,7 +1689,7 @@ static int
w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
{
u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
int err = 0, rc = 0;
int err = 0;
/* Make sure we are using a valid depth */
pict.depth = w9968cf_valid_depth(pict.palette);
......@@ -1767,12 +1750,10 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
else if (cam->filter_type == 2)
reg_v |= 0x000c;
err = w9968cf_write_reg(cam, reg_v, 0x16);
if (err)
if ((err = w9968cf_write_reg(cam, reg_v, 0x16)))
goto error;
rc = w9968cf_sensor_set_picture(cam, pict);
if (rc)
if ((err = w9968cf_sensor_update_picture(cam, pict)))
goto error;
/* If all went well, update the device data structure */
......@@ -1791,10 +1772,7 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
error:
DBG(1, "Failed to change picture settings.")
if (err)
return err;
else
return rc;
return err;
}
......@@ -1809,7 +1787,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
unsigned long fw, fh;
struct ovcamchip_window s_win;
int err=0, rc=0;
int err = 0;
/* Work around to avoid FP arithmetics */
#define __SC(x) ((x) << 10)
......@@ -1853,8 +1831,8 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
ch = h;
}
/* Setup the sensor window */
s_win.format = SENSOR_FORMAT;
/* Setup the window of the sensor */
s_win.format = VIDEO_PALETTE_UYVY;
s_win.width = cam->maxwidth;
s_win.height = cam->maxheight;
s_win.quarter = 0; /* full progressive video */
......@@ -1883,7 +1861,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
}
/* We have to scale win.x and win.y offsets */
/* We have to scale win.x and win.y offsets */
if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
|| (cam->vpp_flag & VPP_UPSCALE) ) {
ax = __SC(win.x)/fw;
......@@ -1914,7 +1892,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
y = ay + s_win.y;
/* Go ! */
if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
goto error;
err += w9968cf_write_reg(cam, scx + x, 0x10);
......@@ -1959,10 +1937,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
error:
DBG(1, "Failed to change the capture area size.")
if (err)
return err;
else
return rc;
return err;
}
......@@ -2177,173 +2152,192 @@ static int
w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
{
struct ovcamchip_control ctl;
int rc;
int err;
ctl.id = cid;
ctl.value = val;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
return rc;
return err;
}
static int
w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val)
w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val)
{
struct ovcamchip_control ctl;
int rc;
int err;
ctl.id = cid;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
if (rc >= 0)
err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
if (!err)
*val = ctl.value;
return rc;
return err;
}
static inline int
w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg)
w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
{
struct i2c_client* c = cam->sensor_client;
int rc = 0;
DBG(6, "Executing CMOS sensor command...")
if (c && c->driver->command)
return c->driver->command(cam->sensor_client, cmd, arg);
else
if (c->driver->command) {
rc = c->driver->command(cam->sensor_client, cmd, arg);
/* The I2C driver returns -EPERM on non-supported controls */
return (rc < 0 && rc != -EPERM) ? rc : 0;
} else
return -ENODEV;
}
/*--------------------------------------------------------------------------
Change some settings of the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Update some settings of the CMOS sensor.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_sensor_change_settings(struct w9968cf_device* cam)
static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
{
int rc;
int err = 0;
/* Auto brightness */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
cam->auto_brt);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
cam->auto_brt);
if (err)
return err;
/* Auto exposure */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
cam->auto_exp);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
cam->auto_exp);
if (err)
return err;
/* Banding filter */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
cam->bandfilt);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
cam->bandfilt);
if (err)
return err;
/* Light frequency */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
cam->lightfreq);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
cam->lightfreq);
if (err)
return err;
/* Back light */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
cam->backlight);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
cam->backlight);
if (err)
return err;
/* Mirror */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
cam->mirror);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
cam->mirror);
if (err)
return err;
return 0;
}
/*--------------------------------------------------------------------------
Get some current picture settings from the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Get some current picture settings from the CMOS sensor and update the
internal 'picture' structure of the camera.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int
w9968cf_sensor_get_picture(struct w9968cf_device* cam,
struct video_picture* pict)
static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
{
int rc, v;
/* Don't return error if a setting is unsupported, or rest of settings
will not be performed */
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->contrast = v;
int err, v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->brightness = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
if (err)
return err;
cam->picture.contrast = v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->colour = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
if (err)
return err;
cam->picture.brightness = v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->hue = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
if (err)
return err;
cam->picture.colour = v;
pict->whiteness = W9968CF_WHITENESS; /* to do! */
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
if (err)
return err;
cam->picture.hue = v;
DBG(5, "Got picture settings from the CMOS sensor.")
PDBGG("Brightness, contrast, hue, colour, whiteness are "
"%d,%d,%d,%d,%d.", pict->brightness, pict->contrast,
pict->hue, pict->colour, pict->whiteness)
"%d,%d,%d,%d,%d.", cam->picture.brightness,cam->picture.contrast,
cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
return 0;
}
/*--------------------------------------------------------------------------
Change picture settings of the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Update picture settings of the CMOS sensor.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int
w9968cf_sensor_set_picture(struct w9968cf_device* cam,
struct video_picture pict)
w9968cf_sensor_update_picture(struct w9968cf_device* cam,
struct video_picture pict)
{
int rc;
rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast);
if (SENSOR_FATAL_ERROR(rc))
return rc;
int err = 0;
if (!cam->auto_brt) {
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
pict.brightness);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if ((!cam->sensor_initialized)
|| pict.contrast != cam->picture.contrast) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT,
pict.contrast);
if (err)
goto fail;
DBG(4, "Contrast changed from %d to %d.",
cam->picture.contrast, pict.contrast)
cam->picture.contrast = pict.contrast;
}
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if (((!cam->sensor_initialized) ||
pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
pict.brightness);
if (err)
goto fail;
DBG(4, "Brightness changed from %d to %d.",
cam->picture.brightness, pict.brightness)
cam->picture.brightness = pict.brightness;
}
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT,
pict.colour);
if (err)
goto fail;
DBG(4, "Colour changed from %d to %d.",
cam->picture.colour, pict.colour)
cam->picture.colour = pict.colour;
}
PDBGG("Brightness, contrast, hue, colour, whiteness are "
"%d,%d,%d,%d,%d.", pict.brightness, pict.contrast,
pict.hue, pict.colour, pict.whiteness)
if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE,
pict.hue);
if (err)
goto fail;
DBG(4, "Hue changed from %d to %d.",
cam->picture.hue, pict.hue)
cam->picture.hue = pict.hue;
}
return 0;
fail:
DBG(4, "Failed to change sensor picture setting.")
return err;
}
......@@ -2353,12 +2347,22 @@ w9968cf_sensor_set_picture(struct w9968cf_device* cam,
****************************************************************************/
/*--------------------------------------------------------------------------
This function is called when the CMOS sensor is detected.
This function is called when a supported CMOS sensor is detected.
Return 0 if the initialization succeeds, a negative number otherwise.
--------------------------------------------------------------------------*/
static void w9968cf_sensor_configure(struct w9968cf_device* cam)
static int w9968cf_sensor_init(struct w9968cf_device* cam)
{
/* NOTE: Make sure width and height are a multiple of 16 */
int err = 0;
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
&cam->monochrome)))
goto error;
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
&cam->sensor)))
goto error;
/* NOTE: Make sure width and height are a multiple of 16 */
switch (cam->sensor_client->addr) {
case OV6xx0_SID:
cam->maxwidth = 352;
......@@ -2372,6 +2376,10 @@ static void w9968cf_sensor_configure(struct w9968cf_device* cam)
cam->minwidth = 64;
cam->minheight = 48;
break;
default:
DBG(1, "Not supported CMOS sensor detected for %s.",
symbolic(camlist, cam->id))
return -EINVAL;
}
/* These values depend on the ones in the ovxxx0.c sources */
......@@ -2390,7 +2398,24 @@ static void w9968cf_sensor_configure(struct w9968cf_device* cam)
cam->hs_polarity = 0;
}
DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor))
if ((err = w9968cf_sensor_update_settings(cam)))
goto error;
if ((err = w9968cf_sensor_update_picture(cam, cam->picture)))
goto error;
cam->sensor_initialized = 1;
DBG(2, "%s CMOS sensor initialized.", symbolic(senlist, cam->sensor))
return 0;
error:
cam->sensor_initialized = 0;
cam->sensor = CC_UNKNOWN;
DBG(1, "CMOS sensor initialization failed for %s (/dev/video%d). "
"Try to detach and attach this device again.",
symbolic(camlist, cam->id), cam->v4ldev->minor)
return err;
}
......@@ -2415,13 +2440,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->usbdev = udev;
cam->id = mod_id;
cam->sensor = CC_UNKNOWN;
strcpy(cam->v4ldev.name, symbolic(camlist, mod_id));
cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev.hardware = VID_HARDWARE_W9968CF;
cam->v4ldev.fops = &w9968cf_fops;
cam->v4ldev.priv = (void*)cam;
cam->v4ldev.minor = video_nr[dev_nr];
cam->sensor_initialized = 0;
/* Calculate the alternate setting number (from 1 to 16)
according to the 'packet_size' module parameter */
......@@ -2433,66 +2452,66 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->max_buffers = (max_buffers[dev_nr] < 2 ||
max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
? W9968CF_BUFFERS : max_buffers[dev_nr];
? W9968CF_BUFFERS : (u8)max_buffers[dev_nr];
cam->double_buffer = (double_buffer[dev_nr] == 0 ||
double_buffer[dev_nr] == 1)
? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER;
? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER;
cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
? clamping[dev_nr] : W9968CF_CLAMPING;
? (u8)clamping[dev_nr] : W9968CF_CLAMPING;
cam->filter_type = (filter_type[dev_nr] == 0 ||
filter_type[dev_nr] == 1 ||
filter_type[dev_nr] == 2)
? filter_type[dev_nr] : W9968CF_FILTER_TYPE;
? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE;
cam->capture = 1;
cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
? largeview[dev_nr] : W9968CF_LARGEVIEW;
? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW;
cam->decompression = (decompression[dev_nr] == 0 ||
decompression[dev_nr] == 1 ||
decompression[dev_nr] == 2)
? decompression[dev_nr] : W9968CF_DECOMPRESSION;
? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION;
cam->upscaling = (upscaling[dev_nr] == 0 ||
upscaling[dev_nr] == 1)
? upscaling[dev_nr] : W9968CF_UPSCALING;
? (u8)upscaling[dev_nr] : W9968CF_UPSCALING;
cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
? autobright[dev_nr] : W9968CF_AUTOBRIGHT;
? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT;
cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
? autoexp[dev_nr] : W9968CF_AUTOEXP;
? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP;
cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
? lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
cam->bandfilt = (bandingfilter[dev_nr] == 0 ||
bandingfilter[dev_nr] == 1)
? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
? backlight[dev_nr] : W9968CF_BACKLIGHT;
? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT;
cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
? clockdiv[dev_nr] : W9968CF_CLOCKDIV;
? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV;
cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
? mirror[dev_nr] : W9968CF_MIRROR;
cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1)
? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO;
cam->picture.brightness = brightness[dev_nr];
cam->picture.hue = hue[dev_nr];
cam->picture.colour = colour[dev_nr];
cam->picture.contrast = contrast[dev_nr];
cam->picture.whiteness = whiteness[dev_nr];
if (w9968cf_valid_palette(force_palette[dev_nr])) {
cam->picture.palette = force_palette[dev_nr];
? (u8)mirror[dev_nr] : W9968CF_MIRROR;
cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1)
? monochrome[dev_nr] : W9968CF_MONOCHROME;
cam->picture.brightness = (u16)brightness[dev_nr];
cam->picture.hue = (u16)hue[dev_nr];
cam->picture.colour = (u16)colour[dev_nr];
cam->picture.contrast = (u16)contrast[dev_nr];
cam->picture.whiteness = (u16)whiteness[dev_nr];
if (w9968cf_valid_palette((u16)force_palette[dev_nr])) {
cam->picture.palette = (u16)force_palette[dev_nr];
cam->force_palette = 1;
} else {
cam->force_palette = 0;
......@@ -2505,7 +2524,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
}
cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
? force_rgb[dev_nr] : W9968CF_FORCE_RGB;
? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
cam->window.x = 0;
cam->window.y = 0;
......@@ -2610,7 +2629,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
else
DBG(3, "- Clock divisor: %d", cam->clockdiv)
if (cam->sensor_mono)
if (cam->monochrome)
DBG(3, "- CMOS sensor used as monochrome.")
else
DBG(3, "- CMOS sensor not used as monochrome.")
......@@ -2626,9 +2645,9 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
{
down(&w9968cf_devlist_sem);
DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor)
DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor)
video_unregister_device(&cam->v4ldev);
video_unregister_device(cam->v4ldev);
list_del(&cam->v4llist);
i2c_del_adapter(&cam->i2c_adapter);
w9968cf_deallocate_memory(cam);
......@@ -2648,24 +2667,25 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
static int w9968cf_open(struct inode* inode, struct file* filp)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
int err;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem);
if (cam->sensor == CC_UNKNOWN) {
DBG(2, "No supported CMOS sensor has been detected by the "
"'ovcamchip' module for the %s (/dev/video%d). Make "
"sure it is loaded *before* the 'w9968cf' module.",
symbolic(camlist, cam->id),cam->v4ldev.minor)
symbolic(camlist, cam->id), cam->v4ldev->minor)
up(&cam->dev_sem);
return -ENODEV;
}
if (cam->users) {
DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.",
symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command)
symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
up(&cam->dev_sem);
return -EWOULDBLOCK;
......@@ -2680,8 +2700,8 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
down(&cam->dev_sem);
}
DBG(5, "Opening the %s, /dev/video%d ...",
symbolic(camlist, cam->id), cam->v4ldev.minor)
DBG(5, "Opening '%s', /dev/video%d ...",
symbolic(camlist, cam->id), cam->v4ldev->minor)
cam->streaming = 0;
cam->misconfigured = 0;
......@@ -2698,7 +2718,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
if ((err = w9968cf_start_transfer(cam)))
goto deallocate_memory;
filp->private_data = (void*)cam;
filp->private_data = cam;
cam->users++;
strcpy(cam->command, current->comm);
......@@ -2720,8 +2740,9 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
static int w9968cf_release(struct inode* inode, struct file* filp)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem); /* prevent disconnect() to be called */
......@@ -2749,11 +2770,12 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
static ssize_t
w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
struct w9968cf_frame_t* fr;
int err = 0;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
if (filp->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
......@@ -2817,9 +2839,8 @@ w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam = (struct w9968cf_device*)
video_get_drvdata(video_devdata(filp));
unsigned long vsize = vma->vm_end - vma->vm_start,
psize = cam->nbuffers * w9968cf_get_max_bufsize(cam),
start = vma->vm_start,
......@@ -2860,10 +2881,11 @@ static int
w9968cf_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
int err;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
if (down_interruptible(&cam->fileop_sem))
return -ERESTARTSYS;
......@@ -2879,7 +2901,7 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
return -EIO;
}
err = w9968cf_do_ioctl(cam, cmd, (void*)arg);
err = w9968cf_v4l_ioctl(inode, filp, cmd, (void* )arg);
up(&cam->fileop_sem);
return err;
......@@ -2887,8 +2909,10 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
static int
w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, void* arg)
{
struct w9968cf_device* cam;
const char* v4l1_ioctls[] = {
"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
"GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
......@@ -2900,7 +2924,9 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
#define V4L1_IOCTL(cmd) \
((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \
v4l1_ioctls[_IOC_NR((cmd))] : "???")
v4l1_ioctls[_IOC_NR((cmd))] : "?")
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
switch (cmd) {
......@@ -2914,7 +2940,7 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
.minheight = cam->minheight,
};
sprintf(cap.name, "W996[87]CF USB Camera #%d",
cam->v4ldev.minor);
cam->v4ldev->minor);
cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present)
? W9968CF_MAX_WIDTH : cam->maxwidth;
cap.maxheight = (cam->upscaling && w9968cf_vppmod_present)
......@@ -2965,15 +2991,10 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGPICT: /* get image properties of the picture */
{
struct video_picture pict;
if (w9968cf_sensor_get_picture(cam, &pict))
if (w9968cf_sensor_get_picture(cam))
return -EIO;
pict.depth = cam->picture.depth;
pict.palette = cam->picture.palette;
if (copy_to_user(arg, &pict, sizeof(pict)))
if (copy_to_user(arg, &cam->picture, sizeof(cam->picture)))
return -EFAULT;
DBG(5, "VIDIOCGPICT successfully called.")
......@@ -3002,13 +3023,6 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
return -EINVAL;
}
if (pict.depth != w9968cf_valid_depth(pict.palette)) {
DBG(4, "Depth %d bpp is not supported for %s palette. "
"VIDIOCSPICT failed.",
pict.depth, symbolic(v4l1_plist, pict.palette))
return -EINVAL;
}
if (!cam->force_palette) {
if (cam->decompression == 0) {
if (w9968cf_need_decompression(pict.palette)) {
......@@ -3027,9 +3041,15 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
}
}
if (pict.palette != cam->picture.palette ||
pict.depth != cam->picture.depth)
{
if (pict.depth != w9968cf_valid_depth(pict.palette)) {
DBG(4, "Requested depth %d bpp is not valid for %s "
"palette: ignored and changed to %d bpp.",
pict.depth, symbolic(v4l1_plist, pict.palette),
w9968cf_valid_depth(pict.palette))
pict.depth = w9968cf_valid_depth(pict.palette);
}
if (pict.palette != cam->picture.palette) {
if(*cam->requested_frame
|| cam->frame_current->queued) {
err = wait_event_interruptible
......@@ -3052,15 +3072,9 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
if (w9968cf_start_transfer(cam))
goto ioctl_fail;
} else if ( ((pict.brightness != cam->picture.brightness) &&
(!cam->auto_brt)) ||
pict.hue != cam->picture.hue ||
pict.colour != cam->picture.colour ||
pict.contrast != cam->picture.contrast ||
pict.whiteness != cam->picture.whiteness ) {
if (w9968cf_sensor_set_picture(cam, pict))
return -EIO;
}
} else if (w9968cf_sensor_update_picture(cam, pict))
return -EIO;
DBG(5, "VIDIOCSPICT successfully called.")
return 0;
......@@ -3092,7 +3106,6 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
win.y != cam->window.y ||
win.width != cam->window.width ||
win.height != cam->window.height) {
if(*cam->requested_frame
|| cam->frame_current->queued) {
err = wait_event_interruptible
......@@ -3203,12 +3216,12 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
}
}
if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
(u16*)&mmap.height)) {
if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
(u16*)&mmap.height))) {
DBG(4, "Resolution not supported (%dx%d). "
"VIDIOCMCAPTURE failed.",
mmap.width, mmap.height)
return -EINVAL;
return err;
}
fr = &cam->frame[mmap.frame];
......@@ -3276,10 +3289,13 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCSYNC: /* wait until the capture of a frame is finished */
{
unsigned int f_num = *((unsigned int *) arg);
unsigned int f_num;
struct w9968cf_frame_t* fr;
int err = 0;
if (copy_from_user(&f_num, arg, sizeof(f_num)))
return -EFAULT;
if (f_num >= cam->nbuffers) {
DBG(4, "Invalid frame number (%d). "
"VIDIOCMCAPTURE failed.", f_num)
......@@ -3323,7 +3339,7 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
{
struct video_unit unit = {
.video = cam->v4ldev.minor,
.video = cam->v4ldev->minor,
.vbi = VIDEO_NO_UNIT,
.radio = VIDEO_NO_UNIT,
.audio = VIDEO_NO_UNIT,
......@@ -3342,9 +3358,8 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGFBUF:
{
struct video_buffer* buffer = (struct video_buffer*)arg;
memset(buffer, 0, sizeof(struct video_buffer));
if (clear_user(arg, sizeof(struct video_buffer)))
return -EFAULT;
DBG(5, "VIDIOCGFBUF successfully called.")
return 0;
......@@ -3470,10 +3485,6 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
else
return -ENODEV;
/* We don't handle multi-config cameras */
if (udev->descriptor.bNumConfigurations != 1)
return -ENODEV;
DBG(2, "%s detected.", symbolic(camlist, mod_id))
if (simcams > W9968CF_MAX_DEVICES)
......@@ -3491,14 +3502,6 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
return -EPERM;
}
err = usb_set_configuration(udev, 1);
err += usb_set_interface(udev, 0, 0);
if (err) {
DBG(1, "Device configuration failed.")
return -EIO;
}
cam = (struct w9968cf_device*)
kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
......@@ -3530,10 +3533,24 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
}
memset(cam->data_buffer, 0, 8);
/* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
/* Register the V4L device */
cam->v4ldev = video_device_alloc();
if (!cam->v4ldev) {
DBG(1, "Could not allocate memory for a V4L structure.")
err = -ENOMEM;
goto fail;
}
err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER,
strcpy(cam->v4ldev->name, symbolic(camlist, mod_id));
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_W9968CF;
cam->v4ldev->fops = &w9968cf_fops;
cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]);
if (err) {
DBG(1, "V4L device registration failed.")
......@@ -3544,13 +3561,15 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev.minor)
DBG(2, "V4L device registered as /dev/video%d.", cam->v4ldev->minor)
/* Ok, add a new entry into the list of V4L registered devices */
/* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
/* Add a new entry into the list of V4L registered devices */
down(&w9968cf_devlist_sem);
list_add(&cam->v4llist, &w9968cf_dev_list);
up(&w9968cf_devlist_sem);
dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
w9968cf_turn_on_led(cam);
......@@ -3559,8 +3578,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
up(&cam->dev_sem);
dev_set_drvdata(&intf->dev, (void*)cam);
usb_set_intfdata(intf, cam);
return 0;
fail: /* Free unused memory */
......@@ -3569,6 +3587,8 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
kfree(cam->control_buffer);
if (cam->data_buffer)
kfree(cam->data_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
up(&cam->dev_sem);
kfree(cam);
}
......@@ -3579,9 +3599,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
static void w9968cf_usb_disconnect(struct usb_interface* intf)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)dev_get_drvdata(&intf->dev);
dev_set_drvdata(&intf->dev, NULL);
(struct w9968cf_device*)usb_get_intfdata(intf);
if (cam) {
/* Prevent concurrent accesses to data */
......@@ -3599,7 +3617,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
DBG(2, "The device is open (/dev/video%d)! "
"Process name: %s. Deregistration and memory "
"deallocation are deferred on close.",
cam->v4ldev.minor, cam->command)
cam->v4ldev->minor, cam->command)
cam->misconfigured = 1;
......@@ -3613,6 +3631,8 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
if (!cam->users)
kfree(cam);
}
usb_set_intfdata(intf, NULL);
}
......
/***************************************************************************
* Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2002-2004 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 *
......@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/config.h>
#include <linux/param.h>
#include <asm/semaphore.h>
#include <asm/types.h>
......@@ -105,7 +106,7 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */
#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */
#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */
#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */
#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */
#define W9968CF_HUE 32768 /* from 0 to 65535 */
#define W9968CF_COLOUR 32768 /* from 0 to 65535 */
......@@ -129,9 +130,10 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
"Dual Mode Camera Chip"
#define W9968CF_MODULE_VERSION "v1.22"
#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia"
#define W9968CF_AUTHOR_EMAIL "<luca_ing@libero.it>"
#define W9968CF_MODULE_VERSION "v1.25-basic"
#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia"
#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define W9968CF_MODULE_LICENSE "GPL"
static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */
......@@ -171,6 +173,7 @@ enum w9968cf_frame_status {
};
struct w9968cf_frame_t {
#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */
void* buffer;
u32 length;
enum w9968cf_frame_status status;
......@@ -194,8 +197,8 @@ struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */
struct w9968cf_device {
enum w9968cf_model_id id; /* private device identifier */
struct video_device v4ldev; /* V4L structure */
struct list_head v4llist; /* entry of the list of V4L cameras */
struct video_device* v4ldev; /* -> V4L structure */
struct list_head v4llist; /* entry of the list of V4L cameras */
struct usb_device* usbdev; /* -> main USB structure */
struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */
......@@ -207,9 +210,9 @@ struct w9968cf_device {
struct w9968cf_frame_t frame_tmp; /* temporary frame */
struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
void* vpp_buffer; /* -> helper buffer for post-processing routines */
void* vpp_buffer; /*-> helper buf.for video post-processing routines */
u8 max_buffers, /* number of requested buffers */
u8 max_buffers, /* number of requested buffers */
force_palette, /* yes=1/no=0 */
force_rgb, /* read RGB instead of BGR, yes=1, no=0 */
double_buffer, /* hardware double buffering yes=1/no=0 */
......@@ -220,15 +223,17 @@ struct w9968cf_device {
decompression, /* 0=disabled, 1=forced, 2=allowed */
upscaling; /* software image scaling, 0=enabled, 1=disabled */
struct video_picture picture; /* current window settings */
struct video_window window; /* current picture settings */
struct video_picture picture; /* current picture settings */
struct video_window window; /* current window settings */
u16 hw_depth, /* depth (used by the chip) */
hw_palette, /* palette (used by the chip) */
hw_width, /* width (used by the chip) */
hw_height, /* height (used by the chip) */
hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */
vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/
start_cropy; /* pixels from VS incative edge to 1st cropped pixel*/
enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
......@@ -239,16 +244,15 @@ struct w9968cf_device {
users, /* flag: number of users holding the device */
streaming; /* flag: yes=1, no=0 */
int sensor; /* type of image CMOS sensor chip (CC_*) */
/* Determined by CMOS sensor type */
u16 maxwidth,
maxheight,
minwidth,
minheight,
start_cropx,
start_cropy;
u8 sensor_initialized; /* flag: yes=1, no=0 */
/* Determined by CMOS sensor type: */
int sensor, /* type of image sensor chip (CC_*) */
monochrome; /* CMOS sensor is (probably) monochrome */
u16 maxwidth, /* maximum width supported by the CMOS sensor */
maxheight, /* maximum height supported by the CMOS sensor */
minwidth, /* minimum width supported by the CMOS sensor */
minheight; /* minimum height supported by the CMOS sensor */
u8 auto_brt, /* auto brightness enabled flag */
auto_exp, /* auto exposure enabled flag */
backlight, /* backlight exposure algorithm flag */
......@@ -256,7 +260,6 @@ struct w9968cf_device {
lightfreq, /* power (lighting) frequency */
bandfilt; /* banding filter enabled flag */
s8 clockdiv; /* clock divisor */
int sensor_mono; /* CMOS sensor is (probably) monochrome */
/* I2C interface to kernel */
struct i2c_adapter i2c_adapter;
......@@ -271,14 +274,9 @@ struct w9968cf_device {
wait_queue_head_t open, wait_queue;
};
#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */
#define SENSOR_FORMAT VIDEO_PALETTE_UYVY
#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
/****************************************************************************
* Macros and other constants *
* Macros for debugging *
****************************************************************************/
#undef DBG
......@@ -294,7 +292,7 @@ if ( ((specific_debug) && (debug == (level))) || \
else if ((level) == 4) \
warn(fmt, ## args); \
else if ((level) >= 5) \
info("[%s,%d] " fmt, \
info("[%s:%d] " fmt, \
__PRETTY_FUNCTION__, __LINE__ , ## args); \
} \
}
......@@ -305,7 +303,7 @@ if ( ((specific_debug) && (debug == (level))) || \
#undef PDBG
#undef PDBGG
#define PDBG(fmt, args...) info("[%s, %d] "fmt, \
#define PDBG(fmt, args...) info("[%s:%d] "fmt, \
__PRETTY_FUNCTION__, __LINE__ , ## args);
#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
......
/***************************************************************************
* Video decoder for the W996[87]CF driver for Linux. *
* *
* Copyright (C) 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2003 2004 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 *
......
/***************************************************************************
* Various definitions for compatibility with external modules. *
* Various definitions for compatibility with OVCAMCHIP external module. *
* This file is part of the W996[87]CF driver for Linux. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* The definitions have been taken from the OVCAMCHIP module written by *
* Mark McClelland. *
* *
* 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 *
......@@ -27,10 +28,8 @@
#include <asm/ioctl.h>
#include <asm/types.h>
/* The following values have been copied from the "ovcamchip" module. */
#ifndef I2C_DRIVERID_OVCAMCHIP
# define I2C_DRIVERID_OVCAMCHIP 0xf00f
# define I2C_DRIVERID_OVCAMCHIP 0xf00f
#endif
/* Controls */
......@@ -77,10 +76,10 @@ struct ovcamchip_window {
int width;
int height;
int format;
int quarter; /* Scale width and height down 2x */
int quarter; /* Scale width and height down 2x */
/* This stuff will be removed eventually */
int clockdiv; /* Clock divisor setting */
int clockdiv; /* Clock divisor setting */
};
/* Commands.
......
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