summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2008-12-29 17:35:39 -0200
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-12-29 17:35:39 -0200
commit64ff6df9dc6bc8b48e19e9c2749625891aa0a509 (patch)
tree514308429e2276e57071fdd5f55bcd342e00bad2 /linux
parenta3f770256b79b9d59770a94bef4d01d4a1390a50 (diff)
parent23d6b9bb8a5ed876d78947ba808e5ba5e6b2cf37 (diff)
downloadmediapointer-dvb-s2-64ff6df9dc6bc8b48e19e9c2749625891aa0a509.tar.gz
mediapointer-dvb-s2-64ff6df9dc6bc8b48e19e9c2749625891aa0a509.tar.bz2
merge: http://linuxtv.org/hg/~gliakhovetski/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux')
-rw-r--r--linux/Documentation/video4linux/CARDLIST.em28xx1
-rw-r--r--linux/Documentation/video4linux/gspca.txt10
-rw-r--r--linux/drivers/media/dvb/dvb-usb/usb-urb.c3
-rw-r--r--linux/drivers/media/dvb/frontends/dib7000p.h9
-rw-r--r--linux/drivers/media/radio/dsbr100.c318
-rw-r--r--linux/drivers/media/radio/radio-mr800.c40
-rw-r--r--linux/drivers/media/video/cs5345.c227
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c461
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-core.c143
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c678
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h16
-rw-r--r--linux/drivers/media/video/gspca/Kconfig1
-rw-r--r--linux/drivers/media/video/gspca/Makefile2
-rw-r--r--linux/drivers/media/video/gspca/conex.c2
-rw-r--r--linux/drivers/media/video/gspca/etoms.c4
-rw-r--r--linux/drivers/media/video/gspca/finepix.c2
-rw-r--r--linux/drivers/media/video/gspca/gspca.c18
-rw-r--r--linux/drivers/media/video/gspca/gspca.h2
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_bridge.h7
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_core.c100
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c135
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h14
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_ov9650.c282
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_ov9650.h164
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_po1030.c166
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_po1030.h10
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c202
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h19
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c213
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h25
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_sensor.h14
-rw-r--r--linux/drivers/media/video/gspca/mars.c2
-rw-r--r--linux/drivers/media/video/gspca/ov519.c4
-rw-r--r--linux/drivers/media/video/gspca/ov534.c116
-rw-r--r--linux/drivers/media/video/gspca/pac207.c7
-rw-r--r--linux/drivers/media/video/gspca/pac7311.c4
-rw-r--r--linux/drivers/media/video/gspca/sonixb.c4
-rw-r--r--linux/drivers/media/video/gspca/sonixj.c2
-rw-r--r--linux/drivers/media/video/gspca/spca500.c4
-rw-r--r--linux/drivers/media/video/gspca/spca501.c2
-rw-r--r--linux/drivers/media/video/gspca/spca505.c2
-rw-r--r--linux/drivers/media/video/gspca/spca506.c2
-rw-r--r--linux/drivers/media/video/gspca/spca508.c2
-rw-r--r--linux/drivers/media/video/gspca/spca561.c535
-rw-r--r--linux/drivers/media/video/gspca/stk014.c2
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/Kconfig9
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/Makefile6
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx.c522
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx.h107
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c533
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h263
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c430
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h297
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h92
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c251
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h315
-rw-r--r--linux/drivers/media/video/gspca/sunplus.c6
-rw-r--r--linux/drivers/media/video/gspca/t613.c4
-rw-r--r--linux/drivers/media/video/gspca/tv8532.c2
-rw-r--r--linux/drivers/media/video/gspca/vc032x.c7
-rw-r--r--linux/drivers/media/video/gspca/zc3xx.c5
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-streams.c2
-rw-r--r--linux/drivers/media/video/msp3400-driver.c2
-rw-r--r--linux/drivers/media/video/stk-webcam.c20
-rw-r--r--linux/drivers/media/video/tda7432.c267
-rw-r--r--linux/drivers/media/video/tda9840.c188
-rw-r--r--linux/drivers/media/video/tda9875.c354
-rw-r--r--linux/drivers/media/video/tea6415c.c49
-rw-r--r--linux/drivers/media/video/tea6420.c49
-rw-r--r--linux/drivers/media/video/tlv320aic23b.c141
-rw-r--r--linux/drivers/media/video/tuner-core.c2
-rw-r--r--linux/drivers/media/video/tvaudio.c715
-rw-r--r--linux/drivers/media/video/tvp5150.c949
-rw-r--r--linux/drivers/media/video/usbvideo/usbvideo.c2
-rw-r--r--linux/drivers/media/video/v4l2-dev.c420
-rw-r--r--linux/drivers/media/video/v4l2-subdev.c10
-rw-r--r--linux/include/media/v4l2-chip-ident.h3
-rw-r--r--linux/include/media/v4l2-dev.h55
-rw-r--r--linux/include/media/v4l2-subdev.h3
79 files changed, 6638 insertions, 3418 deletions
diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx
index 0c4c721da..96b1eb8ac 100644
--- a/linux/Documentation/video4linux/CARDLIST.em28xx
+++ b/linux/Documentation/video4linux/CARDLIST.em28xx
@@ -59,3 +59,4 @@
58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041]
59 -> Pinnacle PCTV HD Mini (em2874) [2304:023f]
60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f]
+ 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840)
diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt
index 81d7d891c..1c58a7630 100644
--- a/linux/Documentation/video4linux/gspca.txt
+++ b/linux/Documentation/video4linux/gspca.txt
@@ -50,9 +50,14 @@ ov519 045e:028c Micro$oft xbox cam
spca508 0461:0815 Micro Innovation IC200
sunplus 0461:0821 Fujifilm MV-1
zc3xx 0461:0a00 MicroInnovation WebCam320
+stv06xx 046d:0840 QuickCam Express
+stv06xx 046d:0850 LEGO cam / QuickCam Web
+stv06xx 046d:0870 Dexxa WebCam USB
spca500 046d:0890 Logitech QuickCam traveler
vc032x 046d:0892 Logitech Orbicam
vc032x 046d:0896 Logitech Orbicam
+vc032x 046d:0897 Logitech QuickCam for Dell notebooks
+zc3xx 046d:089d Logitech QuickCam E2500
zc3xx 046d:08a0 Logitech QC IM
zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound
zc3xx 046d:08a2 Labtec Webcam Pro
@@ -202,7 +207,8 @@ sunplus 08ca:2050 Medion MD 41437
sunplus 08ca:2060 Aiptek PocketDV5300
tv8532 0923:010f ICM532 cams
mars 093a:050f Mars-Semi Pc-Camera
-pac207 093a:2460 PAC207 Qtec Webcam 100
+pac207 093a:2460 Qtec Webcam 100
+pac207 093a:2461 HP Webcam
pac207 093a:2463 Philips SPC 220 NC
pac207 093a:2464 Labtec Webcam 1200
pac207 093a:2468 PAC207
@@ -216,11 +222,13 @@ pac7311 093a:2603 PAC7312
pac7311 093a:2608 Trust WB-3300p
pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350
pac7311 093a:260f SnakeCam
+pac7311 093a:2620 Apollo AC-905
pac7311 093a:2621 PAC731x
pac7311 093a:2622 Genius Eye 312
pac7311 093a:2624 PAC7302
pac7311 093a:2626 Labtec 2200
pac7311 093a:262a Webcam 300k
+pac7311 093a:262c Philips SPC 230 NC
zc3xx 0ac8:0302 Z-star Vimicro zc0302
vc032x 0ac8:0321 Vimicro generic vc0321
vc032x 0ac8:0323 Vimicro Vc0323
diff --git a/linux/drivers/media/dvb/dvb-usb/usb-urb.c b/linux/drivers/media/dvb/dvb-usb/usb-urb.c
index eb3aaaf7a..5becc5a20 100644
--- a/linux/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/linux/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -160,7 +160,8 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
- stream->urb_list[i]->transfer_flags = 0;
+ stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
stream->urbs_initialized++;
}
return 0;
diff --git a/linux/drivers/media/dvb/frontends/dib7000p.h b/linux/drivers/media/dvb/frontends/dib7000p.h
index 3e8126857..aab8112e2 100644
--- a/linux/drivers/media/dvb/frontends/dib7000p.h
+++ b/linux/drivers/media/dvb/frontends/dib7000p.h
@@ -66,7 +66,8 @@ struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
return NULL;
}
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+static inline
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
int no_of_demods, u8 default_addr,
struct dib7000p_config cfg[])
{
@@ -74,13 +75,15 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
return -ENODEV;
}
-extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+static inline
+int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+static inline
+int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
diff --git a/linux/drivers/media/radio/dsbr100.c b/linux/drivers/media/radio/dsbr100.c
index d1865b164..9c8e96f81 100644
--- a/linux/drivers/media/radio/dsbr100.c
+++ b/linux/drivers/media/radio/dsbr100.c
@@ -1,5 +1,5 @@
-/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
- into both the USB and an analog audio input, so this thing
+/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
+ The device plugs into both the USB and an analog audio input, so this thing
only deals with initialisation and frequency setting, the
audio data has to be handled by a sound driver.
@@ -33,6 +33,10 @@
History:
+ Version 0.44:
+ Add suspend/resume functions, fix unplug of device,
+ a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>
+
Version 0.43:
Oliver Neukum: avoided DMA coherency issue
@@ -94,8 +98,8 @@
*/
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-#define DRIVER_VERSION "v0.43"
-#define RADIO_VERSION KERNEL_VERSION(0, 4, 3)
+#define DRIVER_VERSION "v0.44"
+#define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
static struct v4l2_queryctrl radio_qctrl[] = {
{
@@ -146,6 +150,7 @@ devices, that would be 76 and 91. */
#define FREQ_MAX 108.0
#define FREQ_MUL 16000
+#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id);
@@ -162,8 +167,9 @@ module_param(radio_nr, int, 0);
/* Data for one (physical) device */
struct dsbr100_device {
struct usb_device *usbdev;
- struct video_device *videodev;
+ struct video_device videodev;
u8 *transfer_buffer;
+ struct mutex lock; /* buffer locking */
int curfreq;
int stereo;
int users;
@@ -171,7 +177,6 @@ struct dsbr100_device {
int muted;
};
-
static struct usb_device_id usb_dsbr100_device_table [] = {
{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
{ } /* Terminating entry */
@@ -198,95 +203,190 @@ static struct usb_driver usb_dsbr100_driver = {
/* switch on radio */
static int dsbr100_start(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=0;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0xC7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x01, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 0;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
-}
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
+}
/* switch off radio */
static int dsbr100_stop(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=1;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x16, 0x1C, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 1;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
{
+ int retval;
+ int request;
+
freq = (freq / 16 * 80) / 1000 + 856;
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_TUNE,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- (freq >> 8) & 0x00ff, freq & 0xff,
- radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) {
- radio->stereo = -1;
- return -1;
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_TUNE,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ (freq >> 8) & 0x00ff, freq & 0xff,
+ radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_TUNE;
+ goto usb_control_msg_failed;
}
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x96, 0xB7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ radio->stereo = -1;
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
}
/* return the device status. This is, in effect, just whether it
sees a stereo signal or not. Pity. */
static void dsbr100_getstat(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ int retval;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0)
+ 0x00 , 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
radio->stereo = -1;
- else
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, USB_REQ_GET_STATUS);
+ } else {
radio->stereo = !(radio->transfer_buffer[0] & 0x01);
-}
+ }
+ mutex_unlock(&radio->lock);
+}
/* USB subsystem interface begins here */
-/* handle unplugging of the device, release data structures
-if nothing keeps us from doing it. If something is still
-keeping us busy, the release callback of v4l will take care
-of releasing it. */
+/*
+ * Handle unplugging of the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_dsbr100_video_device_release
+ */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
usb_set_intfdata (intf, NULL);
- if (radio) {
- video_unregister_device(radio->videodev);
- radio->videodev = NULL;
- if (radio->users) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- } else {
- radio->removed = 1;
- }
- }
+
+ mutex_lock(&radio->lock);
+ radio->removed = 1;
+ mutex_unlock(&radio->lock);
+
+ video_unregister_device(&radio->videodev);
}
@@ -306,6 +406,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -327,6 +431,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct dsbr100_device *radio = video_drvdata(file);
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -337,9 +447,15 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
radio->curfreq = f->frequency;
- if (dsbr100_setfreq(radio, radio->curfreq) == -1)
+ retval = dsbr100_setfreq(radio, radio->curfreq);
+ if (retval < 0)
dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
return 0;
}
@@ -349,6 +465,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
@@ -373,6 +493,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
@@ -385,17 +509,24 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
- if (dsbr100_stop(radio) == -1) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
}
} else {
- if (dsbr100_start(radio) == -1) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
@@ -447,7 +578,8 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
radio->users = 1;
radio->muted = 1;
- if (dsbr100_start(radio) < 0) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not start up properly\n");
radio->users = 0;
@@ -456,9 +588,9 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
}
retval = dsbr100_setfreq(radio, radio->curfreq);
-
- if (retval == -1)
- printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n");
+ if (retval < 0)
+ dev_warn(&radio->usbdev->dev,
+ "set frequency failed\n");
unlock_kernel();
return 0;
@@ -467,13 +599,19 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
if (!radio)
return -ENODEV;
+
radio->users = 0;
- if (radio->removed) {
- kfree(radio->transfer_buffer);
- kfree(radio);
+ if (!radio->removed) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
+ dev_warn(&radio->usbdev->dev,
+ "dsbr100_stop failed\n");
+ }
+
}
return 0;
}
@@ -485,7 +623,7 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
int retval;
retval = dsbr100_stop(radio);
- if (retval == -1)
+ if (retval < 0)
dev_warn(&intf->dev, "dsbr100_stop failed\n");
dev_info(&intf->dev, "going into suspend..\n");
@@ -500,7 +638,7 @@ static int usb_dsbr100_resume(struct usb_interface *intf)
int retval;
retval = dsbr100_start(radio);
- if (retval == -1)
+ if (retval < 0)
dev_warn(&intf->dev, "dsbr100_start failed\n");
dev_info(&intf->dev, "coming out of suspend..\n");
@@ -508,6 +646,15 @@ static int usb_dsbr100_resume(struct usb_interface *intf)
return 0;
}
+/* free data structures */
+static void usb_dsbr100_video_device_release(struct video_device *videodev)
+{
+ struct dsbr100_device *radio = videodev_to_radio(videodev);
+
+ kfree(radio->transfer_buffer);
+ kfree(radio);
+}
+
/* File system interface */
static const struct file_operations usb_dsbr100_fops = {
.owner = THIS_MODULE,
@@ -536,19 +683,19 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
};
/* V4L2 interface */
-static struct video_device dsbr100_videodev_template = {
+static struct video_device dsbr100_videodev_data = {
.name = "D-Link DSB-R 100",
.fops = &usb_dsbr100_fops,
.ioctl_ops = &usb_dsbr100_ioctl_ops,
- .release = video_device_release,
+ .release = usb_dsbr100_video_device_release,
};
-/* check if the device is present and register with v4l and
-usb if it is */
+/* check if the device is present and register with v4l and usb if it is */
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dsbr100_device *radio;
+ int retval;
radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
@@ -561,23 +708,18 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
kfree(radio);
return -ENOMEM;
}
- radio->videodev = video_device_alloc();
- if (!(radio->videodev)) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- return -ENOMEM;
- }
- memcpy(radio->videodev, &dsbr100_videodev_template,
- sizeof(dsbr100_videodev_template));
+ mutex_init(&radio->lock);
+ radio->videodev = dsbr100_videodev_data;
+
radio->removed = 0;
radio->users = 0;
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = FREQ_MIN * FREQ_MUL;
- video_set_drvdata(radio->videodev, radio);
- if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) {
- dev_warn(&intf->dev, "Could not register video device\n");
- video_device_release(radio->videodev);
+ video_set_drvdata(&radio->videodev, radio);
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register video device\n");
kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;
diff --git a/linux/drivers/media/radio/radio-mr800.c b/linux/drivers/media/radio/radio-mr800.c
index dbce57c26..7683af18d 100644
--- a/linux/drivers/media/radio/radio-mr800.c
+++ b/linux/drivers/media/radio/radio-mr800.c
@@ -142,7 +142,6 @@ struct amradio_device {
unsigned char *buffer;
struct mutex lock; /* buffer locking */
- struct mutex disconnect_lock;
int curfreq;
int stereo;
int users;
@@ -170,7 +169,7 @@ static struct usb_driver usb_amradio_driver = {
.reset_resume = usb_amradio_resume,
#endif
.id_table = usb_amradio_device_table,
- .supports_autosuspend = 1,
+ .supports_autosuspend = 0,
};
/* switch on radio. Send 8 bytes to device. */
@@ -305,16 +304,12 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
{
struct amradio_device *radio = usb_get_intfdata(intf);
- mutex_lock(&radio->disconnect_lock);
+ mutex_lock(&radio->lock);
radio->removed = 1;
- usb_set_intfdata(intf, NULL);
+ mutex_unlock(&radio->lock);
- if (radio->users == 0) {
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- kfree(radio);
- }
- mutex_unlock(&radio->disconnect_lock);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(radio->videodev);
}
/* vidioc_querycap - query device capabilities */
@@ -532,7 +527,7 @@ static int usb_amradio_open(struct inode *inode, struct file *file)
return 0;
}
-/*close device - free driver structures */
+/*close device */
static int usb_amradio_close(struct inode *inode, struct file *file)
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
@@ -541,21 +536,15 @@ static int usb_amradio_close(struct inode *inode, struct file *file)
if (!radio)
return -ENODEV;
- mutex_lock(&radio->disconnect_lock);
radio->users = 0;
- if (radio->removed) {
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- kfree(radio);
- } else {
+ if (!radio->removed) {
retval = amradio_stop(radio);
if (retval < 0)
amradio_dev_warn(&radio->videodev->dev,
"amradio_stop failed\n");
}
- mutex_unlock(&radio->disconnect_lock);
return 0;
}
@@ -612,12 +601,24 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
};
+static void usb_amradio_device_release(struct video_device *videodev)
+{
+ struct amradio_device *radio = video_get_drvdata(videodev);
+
+ /* we call v4l to free radio->videodev */
+ video_device_release(videodev);
+
+ /* free rest memory */
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
/* V4L2 interface */
static struct video_device amradio_videodev_template = {
.name = "AverMedia MR 800 USB FM Radio",
.fops = &usb_amradio_fops,
.ioctl_ops = &usb_amradio_ioctl_ops,
- .release = video_device_release,
+ .release = usb_amradio_device_release,
};
/* check if the device is present and register with v4l and
@@ -655,7 +656,6 @@ static int usb_amradio_probe(struct usb_interface *intf,
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = 95.16 * FREQ_MUL;
- mutex_init(&radio->disconnect_lock);
mutex_init(&radio->lock);
video_set_drvdata(radio->videodev, radio);
diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c
index decbc5f1f..1af0f53dd 100644
--- a/linux/drivers/media/video/cs5345.c
+++ b/linux/drivers/media/video/cs5345.c
@@ -23,9 +23,9 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-i2c-drv.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
#include "compat.h"
MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
@@ -46,111 +46,143 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
-static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
-static inline int cs5345_read(struct i2c_client *client, u8 reg)
+static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = cs5345_read(client, 0x09) & 7;
- route->input |= cs5345_read(client, 0x05) & 0x70;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if ((route->input & 0xf) > 6) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs5345_write(client, 0x09, route->input & 0xf);
- cs5345_write(client, 0x05, route->input & 0xf0);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = cs5345_read(client, 0x07) & 0x3f;
- if (ctrl->value >= 32)
- ctrl->value = ctrl->value - 64;
- break;
-
- case VIDIOC_S_CTRL:
- break;
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 24 || ctrl->value < -24)
- return -EINVAL;
- cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f);
- cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f);
- break;
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cs5345_read(client, reg->reg & 0x1f);
- else
- cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff);
- break;
+ if ((route->input & 0xf) > 6) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
+ return -EINVAL;
}
-#endif
+ cs5345_write(sd, 0x09, route->input & 0xf);
+ cs5345_write(sd, 0x05, route->input & 0xf0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS5345, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs5345_read(client, 0x09) & 7;
- u8 m = cs5345_read(client, 0x04);
- int vol = cs5345_read(client, 0x08) & 0x3f;
-
- v4l_info(client, "Input: %d%s\n", v,
- (m & 0x80) ? " (muted)" : "");
- if (vol >= 32)
- vol = vol - 64;
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
return -EINVAL;
+ ctrl->value = cs5345_read(sd, 0x07) & 0x3f;
+ if (ctrl->value >= 32)
+ ctrl->value = ctrl->value - 64;
+ return 0;
+}
+
+static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0);
+ return 0;
}
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 24 || ctrl->value < -24)
+ return -EINVAL;
+ cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f);
+ cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cs5345_read(sd, reg->reg & 0x1f);
+ return 0;
+}
+
+static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff);
return 0;
}
+#endif
+
+static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0);
+}
+
+static int cs5345_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs5345_read(sd, 0x09) & 7;
+ u8 m = cs5345_read(sd, 0x04);
+ int vol = cs5345_read(sd, 0x08) & 0x3f;
+
+ v4l2_info(sd, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs5345_core_ops = {
+ .log_status = cs5345_log_status,
+ .g_chip_ident = cs5345_g_chip_ident,
+ .g_ctrl = cs5345_g_ctrl,
+ .s_ctrl = cs5345_s_ctrl,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cs5345_g_register,
+ .s_register = cs5345_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops cs5345_audio_ops = {
+ .s_routing = cs5345_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs5345_ops = {
+ .core = &cs5345_core_ops,
+ .audio = &cs5345_audio_ops,
+};
/* ----------------------------------------------------------------------- */
static int cs5345_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
@@ -158,9 +190,25 @@ static int cs5345_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- cs5345_write(client, 0x02, 0x00);
- cs5345_write(client, 0x04, 0x01);
- cs5345_write(client, 0x09, 0x01);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &cs5345_ops);
+
+ cs5345_write(sd, 0x02, 0x00);
+ cs5345_write(sd, 0x04, 0x01);
+ cs5345_write(sd, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -179,6 +227,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_CS5345,
.command = cs5345_command,
.probe = cs5345_probe,
+ .remove = cs5345_remove,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = cs5345_id,
#endif
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index c14fca841..a4ce360b1 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -38,6 +38,8 @@
#include "em28xx.h"
+#define DRIVER_NAME "em28xx"
+
static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");
@@ -46,6 +48,13 @@ static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
+static unsigned long em28xx_devused;
+
struct em28xx_hash_table {
unsigned long hash;
unsigned int model;
@@ -975,8 +984,30 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
} },
},
+ [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+ .name = "Pixelview PlayTV Box 4 USB 2.0",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
[EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
.name = "Pixelview Prolink PlayTV USB 2.0",
+ .has_snapshot_button = 1,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_YMEC_TVF_5533MF,
.decoder = EM28XX_SAA711X,
@@ -1410,6 +1441,7 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
static struct em28xx_hash_table em28xx_eeprom_hash [] = {
/* P/N: SA 60002070465 Tuner: TVF7533-MF */
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
};
@@ -1870,3 +1902,432 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_ir_init(dev);
}
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void request_module_async(void *ptr)
+{
+ struct em28xx *dev = (struct em28xx *)ptr;
+#else
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+#endif
+
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else if (dev->has_alsa_audio)
+ request_module("em28xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("em28xx-dvb");
+}
+
+static void request_modules(struct em28xx *dev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&dev->request_module_wk, request_module_async,
+ (void *)dev);
+#else
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+#endif
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_release_resources(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev)
+ em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
+ /*FIXME: I2C IR should be disconnected */
+
+ em28xx_release_analog_resources(dev);
+
+ em28xx_remove_from_devlist(dev);
+
+ em28xx_i2c_unregister(dev);
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ em28xx_devused &= ~(1 << dev->devno);
+};
+
+/*
+ * em28xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
+ int minor)
+{
+ struct em28xx *dev = *devhandle;
+ int retval = -ENOMEM;
+ int errCode;
+
+ dev->udev = udev;
+ mutex_init(&dev->ctrl_urb_lock);
+ spin_lock_init(&dev->slock);
+ init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
+
+ dev->em28xx_write_regs = em28xx_write_regs;
+ dev->em28xx_read_reg = em28xx_read_reg;
+ dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+ dev->em28xx_write_regs_req = em28xx_write_regs_req;
+ dev->em28xx_read_reg_req = em28xx_read_reg_req;
+ dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+
+ em28xx_pre_card_setup(dev);
+
+ if (!dev->board.is_em2800) {
+ /* Sets I2C speed to 100 KHz */
+ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req failed!"
+ " retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ /* register i2c bus */
+ errCode = em28xx_i2c_register(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ errCode = em28xx_audio_setup(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
+ __func__, errCode);
+ }
+
+ /* wake i2c devices */
+ em28xx_wake_i2c(dev);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+
+#if 0
+ video_set_drvdata(dev->vbi_dev, dev);
+#endif
+
+ if (dev->board.has_msp34xx) {
+ /* Send a reset to other chips via gpio */
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(1) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(2) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+ }
+
+ em28xx_add_into_devlist(dev);
+
+ retval = em28xx_register_analog_devices(dev);
+ if (retval < 0) {
+ em28xx_release_resources(dev);
+ goto fail_reg_devices;
+ }
+
+ em28xx_init_extension(dev);
+
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
+ return 0;
+
+fail_reg_devices:
+ return retval;
+}
+
+/*
+ * em28xx_usb_probe()
+ * checks for supported devices
+ */
+static int em28xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ const struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev;
+ struct usb_interface *uif;
+ struct em28xx *dev = NULL;
+ int retval = -ENODEV;
+ int i, nr, ifnum, isoc_pipe;
+ char *speed;
+ char descr[255] = "";
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+
+ /* Check to see next free device and mark as used */
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ em28xx_devused |= 1<<nr;
+
+ /* Don't register audio interfaces */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+ "interface %i, class %i\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+
+ /* check if the device has the iso in endpoint at the correct place */
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC &&
+ (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) {
+ /* It's a newer em2874/em2875 device */
+ isoc_pipe = 0;
+ } else {
+ int check_interface = 1;
+ isoc_pipe = 1;
+ endpoint = &interface->cur_altsetting->endpoint[1].desc;
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+ USB_ENDPOINT_XFER_ISOC)
+ check_interface = 0;
+
+ if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+ check_interface = 0;
+
+ if (!check_interface) {
+ em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
+ "interface %i, class %i found.\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_err(DRIVER_NAME " This is an anciliary "
+ "interface not used by the driver\n");
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+ }
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ if (udev->manufacturer)
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
+
+ if (udev->product) {
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+ strlcat(descr, udev->product, sizeof(descr));
+ }
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+
+ printk(DRIVER_NAME ": New device %s@ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ descr,
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting->desc.bInterfaceNumber);
+
+ if (nr >= EM28XX_MAXBOARDS) {
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ em28xx_err(DRIVER_NAME ": out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ snprintf(dev->name, 29, "em28xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->alt = -1;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ /* compute alternate max packet sizes */
+ uif = udev->actconfig->interface[0];
+
+ dev->num_alt = uif->num_altsetting;
+ dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
+
+ if (dev->alt_max_pkt_size == NULL) {
+ em28xx_errdev("out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->num_alt ; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ dev->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ }
+
+ if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ dev->model = card[nr];
+
+ /* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ retval = em28xx_init_dev(&dev, udev, nr);
+ if (retval) {
+ em28xx_devused &= ~(1<<dev->devno);
+ kfree(dev);
+
+ return retval;
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ request_modules(dev);
+
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+/*
+ * em28xx_usb_disconnect()
+ * called when the device gets diconencted
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void em28xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ wake_up_interruptible_all(&dev->open);
+
+ if (dev->users) {
+ em28xx_warn
+ ("device /dev/video%d is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ dev->vdev->num);
+
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_uninit_isoc(dev);
+ dev->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_frame);
+ wake_up_interruptible(&dev->wait_stream);
+ } else {
+ dev->state |= DEV_DISCONNECTED;
+ em28xx_release_resources(dev);
+ }
+
+ em28xx_close_extension(dev);
+
+ mutex_unlock(&dev->lock);
+
+ if (!dev->users) {
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ }
+}
+
+static struct usb_driver em28xx_usb_driver = {
+ .name = "em28xx",
+ .probe = em28xx_usb_probe,
+ .disconnect = em28xx_usb_disconnect,
+ .id_table = em28xx_id_table,
+};
+
+static int __init em28xx_module_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&em28xx_usb_driver);
+ if (result)
+ em28xx_err(DRIVER_NAME
+ " usb_register failed. Error number %d.\n", result);
+
+ printk(KERN_INFO DRIVER_NAME " driver loaded\n");
+
+ return result;
+}
+
+static void __exit em28xx_module_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&em28xx_usb_driver);
+}
+
+module_init(em28xx_module_init);
+module_exit(em28xx_module_exit);
diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c
index 025a54bb7..1183519eb 100644
--- a/linux/drivers/media/video/em28xx/em28xx-core.c
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -983,3 +984,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
return 0;
}
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+void em28xx_wake_i2c(struct em28xx *dev)
+{
+ struct v4l2_routing route;
+ int zero = 0;
+
+ route.input = INPUT(dev->ctl_input)->vmux;
+ route.output = 0;
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
+}
+
+/*
+ * Device control list
+ */
+
+static LIST_HEAD(em28xx_devlist);
+static DEFINE_MUTEX(em28xx_devlist_mutex);
+
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio)
+{
+ struct em28xx *h, *dev = NULL;
+ int minor = iminor(inode);
+
+ *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ *has_radio = 0;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(h, &em28xx_devlist, devlist) {
+ if (h->vdev->minor == minor)
+ dev = h;
+ if (h->vbi_dev->minor == minor) {
+ dev = h;
+ *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ dev = h;
+ *has_radio = 1;
+ }
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+
+ return dev;
+}
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_remove_from_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+void em28xx_add_into_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->init(dev);
+ }
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->fini(dev);
+ }
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+void em28xx_init_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
+
+void em28xx_close_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index 4d9b167e6..8be7aadac 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -39,6 +39,7 @@
#include "em28xx.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -47,9 +48,8 @@
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
"Sascha Sommer <saschasommer@freenet.de>"
-#define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
+#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1)
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -72,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-static LIST_HEAD(em28xx_devlist);
-static DEFINE_MUTEX(em28xx_devlist_mutex);
-
-static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);
-MODULE_PARM_DESC(card, "card type");
MODULE_PARM_DESC(video_nr, "video device numbers");
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");
@@ -93,9 +87,6 @@ static unsigned int video_debug;
module_param(video_debug, int, 0644);
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
-static unsigned long em28xx_devused;
-
/* supported video standards */
static struct em28xx_fmt format[] = {
{
@@ -130,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
}
};
-static struct usb_driver em28xx_usb_driver;
-
/* ------------------------------------------------------------------
DMA and thread functions
------------------------------------------------------------------*/
@@ -522,58 +511,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
/********************* v4l2 interface **************************************/
-/*
- * em28xx_config()
- * inits registers with sane defaults
- */
-static int em28xx_config(struct em28xx *dev)
-{
- int retval;
-
- /* Sets I2C speed to 100 KHz */
- if (!dev->board.is_em2800) {
- retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
-#if 1
- /* enable vbi capturing */
-
-/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
-/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
- em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
-#endif
-
- dev->mute = 1; /* maybe not the right place... */
- dev->volume = 0x1f;
-
- em28xx_set_outfmt(dev);
- em28xx_colorlevels_set_default(dev);
- em28xx_compression_disable(dev);
-
- return 0;
-}
-
-/*
- * em28xx_config_i2c()
- * configure i2c attached devices
- */
-static void em28xx_config_i2c(struct em28xx *dev)
-{
- struct v4l2_routing route;
- int zero = 0;
-
- route.input = INPUT(dev->ctl_input)->vmux;
- route.output = 0;
- em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
- em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
-}
-
static void video_mux(struct em28xx *dev, int index)
{
struct v4l2_routing route;
@@ -1226,6 +1163,21 @@ static int em28xx_reg_len(int reg)
}
}
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip);
+
+ return 0;
+}
+
+
static int vidioc_g_register(struct file *file, void *priv,
struct v4l2_register *reg)
{
@@ -1233,7 +1185,8 @@ static int vidioc_g_register(struct file *file, void *priv,
struct em28xx *dev = fh->dev;
int ret;
- if (reg->match_type == V4L2_CHIP_MATCH_AC97) {
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
mutex_lock(&dev->lock);
ret = em28xx_read_ac97(dev, reg->reg);
mutex_unlock(&dev->lock);
@@ -1242,11 +1195,18 @@ static int vidioc_g_register(struct file *file, void *priv,
reg->val = ret;
return 0;
- }
-
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+ }
+ /* Match host */
if (em28xx_reg_len(reg->reg) == 1) {
mutex_lock(&dev->lock);
ret = em28xx_read_reg(dev, reg->reg);
@@ -1279,14 +1239,25 @@ static int vidioc_s_register(struct file *file, void *priv,
__le64 buf;
int rc;
- if (reg->match_type == V4L2_CHIP_MATCH_AC97) {
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
mutex_lock(&dev->lock);
rc = em28xx_write_ac97(dev, reg->reg, reg->val);
mutex_unlock(&dev->lock);
return rc;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
+ return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
}
+ /* Match host */
buf = cpu_to_le64(reg->val);
mutex_lock(&dev->lock);
@@ -1661,28 +1632,12 @@ static int radio_queryctrl(struct file *file, void *priv,
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
- int errCode = 0, radio = 0;
- struct em28xx *h, *dev = NULL;
+ int errCode = 0, radio;
+ struct em28xx *dev;
+ enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
- enum v4l2_buf_type fh_type = 0;
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(h, &em28xx_devlist, devlist) {
- if (h->vdev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- radio = 1;
- dev = h;
- }
- }
- mutex_unlock(&em28xx_devlist_mutex);
+ dev = em28xx_get_device(inode, &fh_type, &radio);
if (NULL == dev)
return -ENODEV;
@@ -1725,7 +1680,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
/* Needed, since GPIO might have disabled power of
some i2c device
*/
- em28xx_config_i2c(dev);
+ em28xx_wake_i2c(dev);
#if 0
/* device needs to be initialized before isoc transfer */
@@ -1756,18 +1711,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
*/
-static void em28xx_release_resources(struct em28xx *dev)
+void em28xx_release_analog_resources(struct em28xx *dev)
{
/*FIXME: I2C IR should be disconnected */
- list_del(&dev->devlist);
- if (dev->sbutton_input_dev)
- em28xx_deregister_snapshot_button(dev);
-
- if (dev->ir)
- em28xx_ir_fini(dev);
-
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
@@ -1793,11 +1741,6 @@ static void em28xx_release_resources(struct em28xx *dev)
video_device_release(dev->vdev);
dev->vdev = NULL;
}
- em28xx_i2c_unregister(dev);
- usb_put_dev(dev->udev);
-
- /* Mark device as unused */
- em28xx_devused &= ~(1<<dev->devno);
}
/*
@@ -1996,6 +1939,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#endif
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
@@ -2051,44 +1995,6 @@ static struct video_device em28xx_radio_template = {
/******************************** usb interface ******************************/
-static LIST_HEAD(em28xx_extension_devlist);
-static DEFINE_MUTEX(em28xx_extension_devlist_lock);
-
-int em28xx_register_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- mutex_lock(&em28xx_extension_devlist_lock);
- list_add_tail(&ops->next, &em28xx_extension_devlist);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->init(dev);
- }
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
- return 0;
-}
-EXPORT_SYMBOL(em28xx_register_extension);
-
-void em28xx_unregister_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->fini(dev);
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
- list_del(&ops->next);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-}
-EXPORT_SYMBOL(em28xx_unregister_extension);
static struct video_device *em28xx_vdev_init(struct em28xx *dev,
const struct video_device *template,
@@ -2111,10 +2017,45 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return vfd;
}
-static int register_analog_devices(struct em28xx *dev)
+int em28xx_register_analog_devices(struct em28xx *dev)
{
int ret;
+ printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
+ dev->name,
+ (EM28XX_VERSION_CODE >> 16) & 0xff,
+ (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+ video_mux(dev, 0);
+
+#if 1
+ /* enable vbi capturing */
+
+/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
+ em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
+#endif
+
+ dev->mute = 1; /* maybe not the right place... */
+ dev->volume = 0x1f;
+
+ em28xx_set_outfmt(dev);
+ em28xx_colorlevels_set_default(dev);
+ em28xx_compression_disable(dev);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+ dev->hscale = 0;
+ dev->vscale = 0;
+
+ /* FIXME: This is a very bad hack! Not all devices have TV on input 2 */
+ dev->ctl_input = 2;
+
/* allocate and fill video video_device struct */
dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
if (!dev->vdev) {
@@ -2163,454 +2104,3 @@ static int register_analog_devices(struct em28xx *dev)
return 0;
}
-
-
-/*
- * em28xx_init_dev()
- * allocates and inits the device structs, registers i2c bus and v4l device
- */
-static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- int minor)
-{
- struct em28xx_ops *ops = NULL;
- struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
- int errCode;
- unsigned int maxh, maxw;
-
- dev->udev = udev;
- mutex_init(&dev->ctrl_urb_lock);
- spin_lock_init(&dev->slock);
- init_waitqueue_head(&dev->open);
- init_waitqueue_head(&dev->wait_frame);
- init_waitqueue_head(&dev->wait_stream);
-
- dev->em28xx_write_regs = em28xx_write_regs;
- dev->em28xx_read_reg = em28xx_read_reg;
- dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
- dev->em28xx_write_regs_req = em28xx_write_regs_req;
- dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
- dev->format = &format[0];
-
- em28xx_pre_card_setup(dev);
-
- errCode = em28xx_config(dev);
- if (errCode) {
- em28xx_errdev("error configuring device\n");
- return -ENOMEM;
- }
-
- /* register i2c bus */
- errCode = em28xx_i2c_register(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
-
- /* Configure audio */
- errCode = em28xx_audio_setup(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
- __func__, errCode);
- }
-
- /* configure the device */
- em28xx_config_i2c(dev);
-
- /* set default norm */
- dev->norm = em28xx_video_template.current_norm;
-
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
-
- /* set default image size */
- dev->width = maxw;
- dev->height = maxh;
- dev->interlaced = EM28XX_INTERLACED_DEFAULT;
- dev->hscale = 0;
- dev->vscale = 0;
- dev->ctl_input = 2;
-
- errCode = em28xx_config(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_config - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
-
-#if 0
- video_set_drvdata(dev->vbi_dev, dev);
-#endif
-
- if (dev->board.has_msp34xx) {
- /* Send a reset to other chips via gpio */
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
-
- errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
- }
-
- video_mux(dev, 0);
-
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- retval = register_analog_devices(dev);
- if (retval < 0) {
- em28xx_release_resources(dev);
- mutex_unlock(&em28xx_devlist_mutex);
- goto fail_reg_devices;
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->id)
- ops->init(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-
- /* Save some power by putting tuner to sleep */
- em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
-
- return 0;
-
-fail_reg_devices:
- return retval;
-}
-
-#if defined(CONFIG_MODULES) && defined(MODULE)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void request_module_async(void *ptr)
-{
- struct em28xx *dev = (struct em28xx *)ptr;
-#else
-static void request_module_async(struct work_struct *work)
-{
- struct em28xx *dev = container_of(work,
- struct em28xx, request_module_wk);
-#endif
-
- if (dev->has_audio_class)
- request_module("snd-usb-audio");
- else if (dev->has_alsa_audio)
- request_module("em28xx-alsa");
-
- if (dev->board.has_dvb)
- request_module("em28xx-dvb");
-}
-
-static void request_modules(struct em28xx *dev)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
- INIT_WORK(&dev->request_module_wk, request_module_async,
- (void *)dev);
-#else
- INIT_WORK(&dev->request_module_wk, request_module_async);
-#endif
- schedule_work(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-/*
- * em28xx_usb_probe()
- * checks for supported devices
- */
-static int em28xx_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- const struct usb_endpoint_descriptor *endpoint;
- struct usb_device *udev;
- struct usb_interface *uif;
- struct em28xx *dev = NULL;
- int retval = -ENODEV;
- int i, nr, ifnum, isoc_pipe;
- char *speed;
- char descr[255] = "";
-
- udev = usb_get_dev(interface_to_usbdev(interface));
- ifnum = interface->altsetting[0].desc.bInterfaceNumber;
-
- /* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- em28xx_devused |= 1<<nr;
-
- /* Don't register audio interfaces */
- if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
- "interface %i, class %i\n",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- endpoint = &interface->cur_altsetting->endpoint[0].desc;
-
- /* check if the device has the iso in endpoint at the correct place */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_ISOC &&
- (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940))
- {
- /* It's a newer em2874/em2875 device */
- isoc_pipe = 0;
- } else {
- int check_interface = 1;
- isoc_pipe = 1;
- endpoint = &interface->cur_altsetting->endpoint[1].desc;
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_ISOC)
- check_interface = 0;
-
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
- check_interface = 0;
-
- if (!check_interface) {
- em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
- "interface %i, class %i found.\n",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_err(DRIVER_NAME " This is an anciliary "
- "interface not used by the driver\n");
-
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
- }
-
- switch (udev->speed) {
- case USB_SPEED_LOW:
- speed = "1.5";
- break;
- case USB_SPEED_UNKNOWN:
- case USB_SPEED_FULL:
- speed = "12";
- break;
- case USB_SPEED_HIGH:
- speed = "480";
- break;
- default:
- speed = "unknown";
- }
-
- if (udev->manufacturer)
- strlcpy(descr, udev->manufacturer, sizeof(descr));
-
- if (udev->product) {
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
- strlcat(descr, udev->product, sizeof(descr));
- }
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
-
- printk(DRIVER_NAME ": New device %s@ %s Mbps "
- "(%04x:%04x, interface %d, class %d)\n",
- descr,
- speed,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting->desc.bInterfaceNumber);
-
- if (nr >= EM28XX_MAXBOARDS) {
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- em28xx_err(DRIVER_NAME ": out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- snprintf(dev->name, 29, "em28xx #%d", nr);
- dev->devno = nr;
- dev->model = id->driver_info;
- dev->alt = -1;
-
- /* Checks if audio is provided by some interface */
- for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
- uif = udev->config->interface[i];
- if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev->has_audio_class = 1;
- break;
- }
- }
-
- /* compute alternate max packet sizes */
- uif = udev->actconfig->interface[0];
-
- dev->num_alt = uif->num_altsetting;
- em28xx_videodbg("Alternate settings: %i\n", dev->num_alt);
-/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */
- dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
-
- if (dev->alt_max_pkt_size == NULL) {
- em28xx_errdev("out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- kfree(dev);
- return -ENOMEM;
- }
-
- for (i = 0; i < dev->num_alt ; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
- wMaxPacketSize);
- dev->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- em28xx_videodbg("Alternate setting %i, max size= %i\n", i,
- dev->alt_max_pkt_size[i]);
- }
-
- if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
- dev->model = card[nr];
-
- /* allocate device struct */
- mutex_init(&dev->lock);
- mutex_lock(&dev->lock);
- retval = em28xx_init_dev(&dev, udev, nr);
- if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
- kfree(dev);
-
- return retval;
- }
-
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
- request_modules(dev);
-
- /* Should be the last thing to do, to avoid newer udev's to
- open the device before fully initializing it
- */
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-/*
- * em28xx_usb_disconnect()
- * called when the device gets diconencted
- * video device will be unregistered on v4l2_close in case it is still open
- */
-static void em28xx_usb_disconnect(struct usb_interface *interface)
-{
- struct em28xx *dev;
- struct em28xx_ops *ops = NULL;
-
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-
- if (!dev)
- return;
-
- em28xx_info("disconnecting %s\n", dev->vdev->name);
-
- /* wait until all current v4l2 io is finished then deallocate
- resources */
- mutex_lock(&dev->lock);
-
- wake_up_interruptible_all(&dev->open);
-
- if (dev->users) {
- em28xx_warn
- ("device /dev/video%d is open! Deregistration and memory "
- "deallocation are deferred on close.\n",
- dev->vdev->num);
-
- dev->state |= DEV_MISCONFIGURED;
- em28xx_uninit_isoc(dev);
- dev->state |= DEV_DISCONNECTED;
- wake_up_interruptible(&dev->wait_frame);
- wake_up_interruptible(&dev->wait_stream);
- } else {
- dev->state |= DEV_DISCONNECTED;
- em28xx_release_resources(dev);
- }
- mutex_unlock(&dev->lock);
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- ops->fini(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
-
- if (!dev->users) {
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
- }
-}
-
-static struct usb_driver em28xx_usb_driver = {
- .name = "em28xx",
- .probe = em28xx_usb_probe,
- .disconnect = em28xx_usb_disconnect,
- .id_table = em28xx_id_table,
-};
-
-static int __init em28xx_module_init(void)
-{
- int result;
-
- printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
- (EM28XX_VERSION_CODE >> 16) & 0xff,
- (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
- SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
-#endif
-
- /* register this driver with the USB subsystem */
- result = usb_register(&em28xx_usb_driver);
- if (result)
- em28xx_err(DRIVER_NAME
- " usb_register failed. Error number %d.\n", result);
-
- return result;
-}
-
-static void __exit em28xx_module_exit(void)
-{
- /* deregister this driver with the USB subsystem */
- usb_deregister(&em28xx_usb_driver);
-}
-
-module_init(em28xx_module_init);
-module_exit(em28xx_module_exit);
-
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index 84f49934a..93c47f260 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -100,6 +100,7 @@
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
#define EM2874_BOARD_PINNACLE_PCTV_80E 59
#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
+#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -594,10 +595,20 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
-
-/* Provided by em28xx-video.c */
+void em28xx_wake_i2c(struct em28xx *dev);
+void em28xx_remove_from_devlist(struct em28xx *dev);
+void em28xx_add_into_devlist(struct em28xx *dev);
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
+void em28xx_init_extension(struct em28xx *dev);
+void em28xx_close_extension(struct em28xx *dev);
+
+/* Provided by em28xx-video.c */
+int em28xx_register_analog_devices(struct em28xx *dev);
+void em28xx_release_analog_resources(struct em28xx *dev);
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device *udev, int model);
@@ -608,6 +619,7 @@ extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
+void em28xx_release_resources(struct em28xx *dev);
/* Provided by em28xx-input.c */
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig
index 770fb699d..ee6a691df 100644
--- a/linux/drivers/media/video/gspca/Kconfig
+++ b/linux/drivers/media/video/gspca/Kconfig
@@ -18,6 +18,7 @@ menuconfig USB_GSPCA
if USB_GSPCA && VIDEO_V4L2
source "drivers/media/video/gspca/m5602/Kconfig"
+source "drivers/media/video/gspca/stv06xx/Kconfig"
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"
diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile
index 6c8046e23..bd8d9ee40 100644
--- a/linux/drivers/media/video/gspca/Makefile
+++ b/linux/drivers/media/video/gspca/Makefile
@@ -47,4 +47,4 @@ gspca_vc032x-objs := vc032x.o
gspca_zc3xx-objs := zc3xx.o
obj-$(CONFIG_USB_M5602) += m5602/
-
+obj-$(CONFIG_USB_STV06XX) += stv06xx/
diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c
index de28354ea..1753f5bb3 100644
--- a/linux/drivers/media/video/gspca/conex.c
+++ b/linux/drivers/media/video/gspca/conex.c
@@ -93,7 +93,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c
index 28869ef26..84b56c303 100644
--- a/linux/drivers/media/video/gspca/etoms.c
+++ b/linux/drivers/media/video/gspca/etoms.c
@@ -112,7 +112,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240,
@@ -125,7 +125,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0}, */
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c
index 03cb94466..afc8b2dd3 100644
--- a/linux/drivers/media/video/gspca/finepix.c
+++ b/linux/drivers/media/video/gspca/finepix.c
@@ -72,7 +72,7 @@ struct usb_fpix {
}
/* These cameras only support 320x200. */
-static struct v4l2_pix_format fpix_mode[1] = {
+static const struct v4l2_pix_format fpix_mode[1] = {
{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index 71e7afe1b..5adbe7301 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -785,8 +785,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int mode;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
mode = gspca_dev->curr_mode;
memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
sizeof fmt->fmt.pix);
@@ -798,8 +796,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
{
int w, h, mode, mode2;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
@@ -1153,8 +1149,6 @@ static int vidioc_reqbufs(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int i, ret = 0;
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
switch (rb->memory) {
case GSPCA_MEMORY_READ: /* (internal call) */
case V4L2_MEMORY_MMAP:
@@ -1219,8 +1213,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
struct gspca_frame *frame;
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || v4l2_buf->index < 0
+ if (v4l2_buf->index < 0
|| v4l2_buf->index >= gspca_dev->nframes)
return -EINVAL;
@@ -1243,7 +1236,8 @@ static int vidioc_streamon(struct file *file, void *priv,
ret = -ENODEV;
goto out;
}
- if (gspca_dev->nframes == 0) {
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
ret = -EINVAL;
goto out;
}
@@ -1561,8 +1555,6 @@ static int vidioc_dqbuf(struct file *file, void *priv,
int i, ret;
PDEBUG(D_FRAM, "dqbuf");
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (v4l2_buf->memory != gspca_dev->memory)
return -EINVAL;
@@ -1617,8 +1609,6 @@ static int vidioc_qbuf(struct file *file, void *priv,
int i, index, ret;
PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
@@ -1828,7 +1818,7 @@ static struct file_operations dev_fops = {
.release = dev_close,
.read = dev_read,
.mmap = dev_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = __video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
#endif
diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h
index 79cef31a5..c90af9cb1 100644
--- a/linux/drivers/media/video/gspca/gspca.h
+++ b/linux/drivers/media/video/gspca/gspca.h
@@ -56,7 +56,7 @@ extern int gspca_debug;
/* device information - set at probe time */
struct cam {
int bulk_size; /* buffer size when image transfer by bulk */
- struct v4l2_pix_format *cam_mode; /* size nmodes */
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
__u8 bulk_nurbs; /* number of URBs in bulk mode
* - cannot be > MAX_NURBS
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h
index c1c7ce524..a3f3b7a0c 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -109,7 +109,6 @@ static const unsigned char sensor_urb_skeleton[] = {
0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
};
-/* m5602 device descriptor, currently it just wraps the m5602_camera struct */
struct sd {
struct gspca_dev gspca_dev;
@@ -134,4 +133,10 @@ int m5602_read_bridge(
int m5602_write_bridge(
struct sd *sd, u8 address, u8 i2c_data);
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c
index fd6ce384b..ed906fe31 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c
@@ -24,7 +24,7 @@
/* Kernel module parameters */
int force_sensor;
-int dump_bridge;
+static int dump_bridge;
int dump_sensor;
static const __devinitdata struct usb_device_id m5602_table[] = {
@@ -80,6 +80,97 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
return (err < 0) ? err : 0;
}
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ if (!len || len > sd->sensor->i2c_regW)
+ return -EINVAL;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ if (sd->sensor->i2c_regW == 1) {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, len);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+ if (err < 0)
+ goto out;
+ } else {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+ }
+
+ for (i = 0; (i < len) && !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(D_CONF, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+out:
+ return err;
+}
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > sd->sensor->i2c_regW || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
/* Dump all the registers of the m5602 bridge,
unfortunately this breaks the camera until it's power cycled */
static void m5602_dump_bridge(struct sd *sd)
@@ -150,11 +241,15 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
/* Send start command to the camera */
const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+
+ if (sd->sensor->start)
+ sd->sensor->start(sd);
+
memcpy(buf, buffer, sizeof(buffer));
err = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x04, 0x40, 0x19, 0x0000, buf,
- 4, M5602_URB_MSG_TIMEOUT);
+ sizeof(buffer), M5602_URB_MSG_TIMEOUT);
PDEBUG(D_STREAM, "Transfer started");
return (err < 0) ? err : 0;
@@ -284,6 +379,7 @@ static int __init mod_m5602_init(void)
PDEBUG(D_PROBE, "registered");
return 0;
}
+
static void __exit mod_m5602_exit(void)
{
usb_deregister(&sd_driver);
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index fb700c2d0..c0e71c331 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -18,6 +18,8 @@
#include "m5602_mt9m111.h"
+static void mt9m111_dump_registers(struct sd *sd);
+
int mt9m111_probe(struct sd *sd)
{
u8 data[2] = {0x00, 0x00};
@@ -44,12 +46,12 @@ int mt9m111_probe(struct sd *sd)
} else {
data[0] = preinit_mt9m111[i][2];
data[1] = preinit_mt9m111[i][3];
- mt9m111_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_mt9m111[i][1], data, 2);
}
}
- if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
@@ -72,7 +74,7 @@ int mt9m111_init(struct sd *sd)
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
u8 data[2];
if (init_mt9m111[i][0] == BRIDGE) {
@@ -82,7 +84,7 @@ int mt9m111_init(struct sd *sd)
} else {
data[0] = init_mt9m111[i][2];
data[1] = init_mt9m111[i][3];
- err = mt9m111_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_mt9m111[i][1], data, 2);
}
}
@@ -104,12 +106,12 @@ int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_ROWS;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -121,19 +123,19 @@ int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfe) | val;
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -142,12 +144,12 @@ int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_COLS;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -159,19 +161,19 @@ int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02);
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -180,7 +182,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
tmp = ((data[1] << 8) | data[0]);
*val = ((tmp & (1 << 10)) * 2) |
@@ -190,7 +192,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -200,7 +202,7 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
@@ -225,90 +227,13 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
data[1], data[0]);
- err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
data, 2);
out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len) {
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a);
- if (err < 0)
- goto out;
-
- for (i = 0; i < len && !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x contains 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger
- than 16 bits has yet been seen, nor with 0 :p*/
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
+ return err;
}
-void mt9m111_dump_registers(struct sd *sd)
+static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
@@ -316,27 +241,27 @@ void mt9m111_dump_registers(struct sd *sd)
info("Dumping the mt9m111 sensor core registers");
value[1] = MT9M111_SENSOR_CORE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 color pipeline registers");
value[1] = MT9M111_COLORPIPE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 camera control registers");
value[1] = MT9M111_CAMERA_CONTROL;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h
index 315209d5a..e795ab7a3 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -87,14 +87,6 @@ int mt9m111_probe(struct sd *sd);
int mt9m111_init(struct sd *sd);
int mt9m111_power_down(struct sd *sd);
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void mt9m111_dump_registers(struct sd *sd);
-
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -106,14 +98,12 @@ static struct m5602_sensor mt9m111 = {
.name = "MT9M111",
.i2c_slave_id = 0xba,
+ .i2c_regW = 2,
.probe = mt9m111_probe,
.init = mt9m111_init,
.power_down = mt9m111_power_down,
- .read_sensor = mt9m111_read_sensor,
- .write_sensor = mt9m111_write_sensor,
-
.nctrls = 3,
.ctrls = {
{
@@ -1003,7 +993,7 @@ static const unsigned char init_mt9m111[][4] =
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, /* 639*/
{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 07ef2b3db..dde5075ed 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -47,86 +47,30 @@ static
}
},
{
+ .ident = "ASUS A6Ja",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
+ }
+ },
+ {
.ident = "ASUS A6Kt",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
}
},
+ {
+ .ident = "Alienware Aurora m9700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
+ }
+ },
{ }
};
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- /* The ov9650 registers have a max depth of one byte */
- if (len > 1 || !len)
- return -EINVAL;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- ov9650.i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The ov9650 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
+static void ov9650_dump_registers(struct sd *sd);
int ov9650_probe(struct sd *sd)
{
@@ -148,16 +92,16 @@ int ov9650_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
u8 data = preinit_ov9650[i][2];
if (preinit_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_ov9650[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_ov9650[i][1], data);
}
- if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
return -ENODEV;
- if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
@@ -186,34 +130,90 @@ int ov9650_init(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
data = init_ov9650[i][2];
if (init_ov9650[i][0] == SENSOR)
- err = ov9650_write_sensor(sd, init_ov9650[i][1],
+ err = m5602_write_sensor(sd, init_ov9650[i][1],
&data, 1);
else
err = m5602_write_bridge(sd, init_ov9650[i][1], data);
}
- if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+ if (dmi_check_system(ov9650_flip_dmi_table) && !err) {
info("vflip quirk active");
data = 0x30;
- err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &data, 1);
}
+ return err;
+}
- return (err < 0) ? err : 0;
+int ov9650_start(struct sd *sd)
+{
+ int i, err = 0;
+ struct cam *cam = &sd->gspca_dev.cam;
+
+ for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
+ u8 data = res_init_ov9650[i][1];
+ err = m5602_write_bridge(sd, res_init_ov9650[i][0], data);
+ }
+ if (err < 0)
+ return err;
+
+ switch (cam->cam_mode[sd->gspca_dev.curr_mode].width)
+ {
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(VGA_ov9650) && !err; i++) {
+ u8 data = VGA_ov9650[i][2];
+ if (VGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ VGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, VGA_ov9650[i][1], data);
+ }
+ break;
+
+ case 352:
+ PDEBUG(D_V4L2, "Configuring camera for CIF mode");
+
+ for (i = 0; i < ARRAY_SIZE(CIF_ov9650) && !err; i++) {
+ u8 data = CIF_ov9650[i][2];
+ if (CIF_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ CIF_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, CIF_ov9650[i][1], data);
+ }
+ break;
+
+ case 320:
+ PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(QVGA_ov9650) && !err; i++) {
+ u8 data = QVGA_ov9650[i][2];
+ if (QVGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ QVGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, QVGA_ov9650[i][1], data);
+ }
+ break;
+ }
+ return err;
}
int ov9650_power_down(struct sd *sd)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+ int i, err = 0;
+ for (i = 0; i < ARRAY_SIZE(power_down_ov9650) && !err; i++) {
u8 data = power_down_ov9650[i][2];
if (power_down_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ err = m5602_write_sensor(sd,
power_down_ov9650[i][1], &data, 1);
else
- m5602_write_bridge(sd, power_down_ov9650[i][1], data);
+ err = m5602_write_bridge(sd, power_down_ov9650[i][1],
+ data);
}
- return 0;
+ return err;
}
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -222,24 +222,24 @@ int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
if (err < 0)
goto out;
*val = i2c_data & 0x03;
- err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data << 2);
- err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data & 0x3f) << 10;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -253,24 +253,24 @@ int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
/* The 6 MSBs */
i2c_data = (val >> 10) & 0x3f;
- err = ov9650_write_sensor(sd, OV9650_AECHM,
+ err = m5602_write_sensor(sd, OV9650_AECHM,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 8 middle bits */
i2c_data = (val >> 2) & 0xff;
- err = ov9650_write_sensor(sd, OV9650_AECH,
+ err = m5602_write_sensor(sd, OV9650_AECH,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 2 LSBs */
i2c_data = val & 0x03;
- err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -279,13 +279,13 @@ int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -297,16 +297,16 @@ int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
/* The 2 MSB */
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) |
(i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return err;
}
int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -315,12 +315,12 @@ int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_RED, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -333,9 +333,9 @@ int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -344,12 +344,12 @@ int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -362,9 +362,9 @@ int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -373,14 +373,14 @@ int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1;
else
*val = (i2c_data & OV9650_HFLIP) >> 5;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -390,20 +390,20 @@ int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
if (dmi_check_system(ov9650_flip_dmi_table))
i2c_data = ((i2c_data & 0xdf) |
- (((val ? 0 : 1) & 0x01) << 5));
+ (((val ? 0 : 1) & 0x01) << 5));
else
i2c_data = ((i2c_data & 0xdf) |
- ((val & 0x01) << 5));
+ ((val & 0x01) << 5));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -412,14 +412,14 @@ int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & 0x10) >> 4) ? 0 : 1;
else
*val = (i2c_data & 0x10) >> 4;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -429,7 +429,7 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
@@ -440,9 +440,9 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = ((i2c_data & 0xef) |
((val & 0x01) << 4));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -451,16 +451,16 @@ int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -473,22 +473,22 @@ int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -497,11 +497,11 @@ int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AWB_EN) >> 1;
PDEBUG(D_V4L2, "Read auto white balance %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -511,14 +511,14 @@ int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -527,11 +527,11 @@ int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AGC_EN) >> 2;
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -541,23 +541,23 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void ov9650_dump_registers(struct sd *sd)
+static void ov9650_dump_registers(struct sd *sd)
{
int address;
info("Dumping the ov9650 register state");
for (address = 0; address < 0xa9; address++) {
u8 value;
- ov9650_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -569,9 +569,9 @@ void ov9650_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- ov9650_read_sensor(sd, address, &old_value, 1);
- ov9650_write_sensor(sd, address, test_value, 1);
- ov9650_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -579,6 +579,6 @@ void ov9650_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- ov9650_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h
index e0efdb930..f4b33b8e8 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -20,7 +20,6 @@
#define M5602_OV9650_H_
#include <linux/dmi.h>
-
#include "m5602_sensor.h"
/*****************************************************************************/
@@ -36,6 +35,7 @@
#define OV9650_PID 0x0a
#define OV9650_VER 0x0b
#define OV9650_COM3 0x0c
+#define OV9650_COM4 0x0d
#define OV9650_COM5 0x0e
#define OV9650_COM6 0x0f
#define OV9650_AECH 0x10
@@ -94,6 +94,8 @@
#define OV9650_REGISTER_RESET (1 << 7)
#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_CIF_SELECT (1 << 5)
+#define OV9650_QVGA_SELECT (1 << 4)
#define OV9650_RGB_SELECT (1 << 2)
#define OV9650_RAW_RGB_SELECT (1 << 0)
@@ -108,6 +110,8 @@
#define OV9650_SYSTEM_CLK_SEL (1 << 7)
#define OV9650_SLAM_MODE (1 << 4)
+#define OV9650_QVGA_VARIOPIXEL (1 << 7)
+
#define OV9650_VFLIP (1 << 4)
#define OV9650_HFLIP (1 << 5)
@@ -124,15 +128,9 @@ extern int dump_sensor;
int ov9650_probe(struct sd *sd);
int ov9650_init(struct sd *sd);
+int ov9650_start(struct sd *sd);
int ov9650_power_down(struct sd *sd);
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void ov9650_dump_registers(struct sd *sd);
-
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -155,11 +153,11 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static struct m5602_sensor ov9650 = {
.name = "OV9650",
.i2c_slave_id = 0x60,
+ .i2c_regW = 1,
.probe = ov9650_probe,
.init = ov9650_init,
+ .start = ov9650_start,
.power_down = ov9650_power_down,
- .read_sensor = ov9650_read_sensor,
- .write_sensor = ov9650_write_sensor,
.nctrls = 8,
.ctrls = {
@@ -264,18 +262,38 @@ static struct m5602_sensor ov9650 = {
}
},
- .nmodes = 1,
+ .nmodes = 3,
.modes = {
{
- M5602_DEFAULT_FRAME_WIDTH,
- M5602_DEFAULT_FRAME_HEIGHT,
+ 320,
+ 240,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }, {
+ 352,
+ 288,
V4L2_PIX_FMT_SBGGR8,
V4L2_FIELD_NONE,
.sizeimage =
- M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
- .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ 352 * 288,
+ .bytesperline = 352,
.colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1
+ .priv = 0
+ }, {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
}
}
};
@@ -324,6 +342,7 @@ static const unsigned char init_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
/* Reset chip */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* Enable double clock */
@@ -331,8 +350,6 @@ static const unsigned char init_ov9650[][3] =
/* Do something out of spec with the power */
{SENSOR, OV9650_OFON, 0x40},
- /* Set QQVGA */
- {SENSOR, OV9650_COM1, 0x20},
/* Set fast AGC/AEC algorithm with unlimited step size */
{SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
OV9650_AEC_UNLIM_STEP_SIZE |
@@ -343,7 +360,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_ACOM38, 0x81},
/* Turn off color matrix coefficient double option */
{SENSOR, OV9650_COM16, 0x00},
- /* Enable color matrix for RGB/YUV, Delay Y channel,
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
set output Y/UV delay to 1 */
{SENSOR, OV9650_COM13, 0x19},
/* Enable digital BLC, Set output mode to U Y V Y */
@@ -352,7 +369,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_COM24, 0x00},
/* Enable HREF and some out of spec things */
{SENSOR, OV9650_COM12, 0x73},
- /* Set all DBLC offset signs to positive and
+ /* Set all DBLC offset signs to positive and
do some out of spec stuff */
{SENSOR, OV9650_DBLC1, 0xdf},
{SENSOR, OV9650_COM21, 0x06},
@@ -364,7 +381,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_RSVD96, 0x04},
/* Enable full range output */
{SENSOR, OV9650_COM15, 0x0},
- /* Enable HREF at optical black, enable ADBLC bias,
+ /* Enable HREF at optical black, enable ADBLC bias,
enable ADBLC, reset timings at format change */
{SENSOR, OV9650_COM6, 0x4b},
/* Subtract 32 from the B channel bias */
@@ -385,7 +402,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_AEB, 0x5c},
/* Set the high and low limit nibbles to 3 */
{SENSOR, OV9650_VPT, 0xc3},
- /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
drop VSYNC at frame drop,
limit exposure timing,
drop frame when the AEC step is larger than the exposure gap */
@@ -394,9 +411,9 @@ static const unsigned char init_ov9650[][3] =
and set PWDN to SLVS (slave mode vertical sync) */
{SENSOR, OV9650_COM10, 0x42},
/* Set horizontal column start high to default value */
- {SENSOR, OV9650_HSTART, 0x1a},
+ {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
/* Set horizontal column end */
- {SENSOR, OV9650_HSTOP, 0xbf},
+ {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
/* Complementing register to the two writes above */
{SENSOR, OV9650_HREF, 0xb2},
/* Set vertical row start high bits */
@@ -405,10 +422,6 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_VSTOP, 0x7e},
/* Set complementing vertical frame control */
{SENSOR, OV9650_VREF, 0x10},
- /* Set raw RGB output format with VGA resolution */
- {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
- OV9650_RGB_SELECT |
- OV9650_RAW_RGB_SELECT},
{SENSOR, OV9650_ADC, 0x04},
{SENSOR, OV9650_HV, 0x40},
/* Enable denoise, and white-pixel erase */
@@ -417,31 +430,15 @@ static const unsigned char init_ov9650[][3] =
/* Set the high bits of the exposure value */
{SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+ /* Enable VARIOPIXEL */
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
/* Set the low bits of the exposure value */
{SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
{SENSOR, OV9650_GAIN, GAIN_DEFAULT},
{SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
{SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
-
- {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
- {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL},
-
- {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
- {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
- {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
- {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
- {BRIDGE, M5602_XB_SIG_INI, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- /* Moves the view window in a vertical orientation */
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
};
static const unsigned char power_down_ov9650[][3] =
@@ -461,7 +458,76 @@ static const unsigned char power_down_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
- {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+};
+
+static const unsigned char res_init_ov9650[][2] =
+{
+ {M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {M5602_XB_PIX_OF_LINE_H, 0x82},
+ {M5602_XB_PIX_OF_LINE_L, 0x00},
+ {M5602_XB_SIG_INI, 0x01}
+};
+
+static const unsigned char VGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, /* 640 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xe2},
+
+ {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char CIF_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x20}, /* 288 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 352 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xc2},
+
+ {SENSOR, OV9650_COM7, OV9650_CIF_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char QVGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xf0}, /* 240 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x31}, /* 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 320 + 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x71},
+
+ {SENSOR, OV9650_COM7, OV9650_QVGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
};
#endif
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c
index d17ac5256..2e7fb9167 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -18,6 +18,8 @@
#include "m5602_po1030.h"
+static void po1030_dump_registers(struct sd *sd);
+
int po1030_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0, i;
@@ -38,16 +40,16 @@ int po1030_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
u8 data = preinit_po1030[i][2];
if (preinit_po1030[i][0] == SENSOR)
- po1030_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_po1030[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_po1030[i][1], data);
}
- if (po1030_read_sensor(sd, 0x3, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x3, &prod_id, 1))
return -ENODEV;
- if (po1030_read_sensor(sd, 0x4, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x4, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x02) && (ver_id == 0xef)) {
@@ -64,78 +66,12 @@ sensor_found:
return 0;
}
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The po1030 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the footer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int po1030_init(struct sd *sd)
{
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
u8 data[2] = {0x00, 0x00};
switch (init_po1030[i][0]) {
@@ -147,16 +83,10 @@ int po1030_init(struct sd *sd)
case SENSOR:
data[0] = init_po1030[i][2];
- err = po1030_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_po1030[i][1], data, 1);
break;
- case SENSOR_LONG:
- data[0] = init_po1030[i][2];
- data[1] = init_po1030[i][3];
- err = po1030_write_sensor(sd,
- init_po1030[i][1], data, 2);
- break;
default:
info("Invalid stream command, exiting init");
return -EINVAL;
@@ -166,7 +96,7 @@ int po1030_init(struct sd *sd)
if (dump_sensor)
po1030_dump_registers(sd);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -175,19 +105,19 @@ int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data << 8);
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Exposure read as %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -202,7 +132,7 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
@@ -210,11 +140,11 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = (val & 0xff);
PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -223,12 +153,12 @@ int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read global gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -237,14 +167,14 @@ int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_CONTROL2,
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2,
&i2c_data, 1);
*val = (i2c_data >> 7) & 0x01 ;
PDEBUG(D_V4L2, "Read hflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -254,13 +184,17 @@ int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set hflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 7;
+ i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -269,14 +203,14 @@ int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = (i2c_data >> 6) & 0x01;
PDEBUG(D_V4L2, "Read vflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -286,13 +220,17 @@ int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 6;
+ i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -303,9 +241,9 @@ int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -314,11 +252,11 @@ int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -329,9 +267,9 @@ int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -340,12 +278,12 @@ int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -355,10 +293,10 @@ int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
int err;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_power_down(struct sd *sd)
@@ -366,14 +304,14 @@ int po1030_power_down(struct sd *sd)
return 0;
}
-void po1030_dump_registers(struct sd *sd)
+static void po1030_dump_registers(struct sd *sd)
{
int address;
u8 value = 0;
info("Dumping the po1030 sensor core registers");
for (address = 0; address < 0x7f; address++) {
- po1030_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -385,9 +323,9 @@ void po1030_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- po1030_read_sensor(sd, address, &old_value, 1);
- po1030_write_sensor(sd, address, test_value, 1);
- po1030_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -395,6 +333,6 @@ void po1030_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- po1030_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h
index a0b75ff61..def39d5bc 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -10,7 +10,7 @@
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
- * Register defines taken from Pascal Stangs Proxycon Armlib
+ * Register defines taken from Pascal Stangs Procyon Armlib
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -128,13 +128,6 @@ int po1030_probe(struct sd *sd);
int po1030_init(struct sd *sd);
int po1030_power_down(struct sd *sd);
-void po1030_dump_registers(struct sd *sd);
-
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -152,6 +145,7 @@ static struct m5602_sensor po1030 = {
.name = "PO1030",
.i2c_slave_id = 0xdc,
+ .i2c_regW = 1,
.probe = po1030_probe,
.init = po1030_init,
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index 1f72e7eae..2bd7d4d99 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -52,6 +52,7 @@ static
{ }
};
+static void s5k4aa_dump_registers(struct sd *sd);
int s5k4aa_probe(struct sd *sd)
{
@@ -84,7 +85,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR:
data[0] = preinit_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 1);
break;
@@ -92,7 +93,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR_LONG:
data[0] = preinit_s5k4aa[i][2];
data[1] = preinit_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 2);
break;
@@ -103,13 +104,14 @@ int s5k4aa_probe(struct sd *sd)
}
/* Test some registers, but we don't know their exact meaning yet */
- if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
+ if (m5602_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
return -ENODEV;
if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
return -ENODEV;
else
info("Detected a s5k4aa sensor");
+
sensor_found:
sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
@@ -119,90 +121,6 @@ sensor_found:
return 0;
}
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- for (i = 0; (i < len) & !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k4aa_init(struct sd *sd)
{
int i, err = 0;
@@ -219,14 +137,14 @@ int s5k4aa_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k4aa[i][2];
data[1] = init_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 2);
break;
default:
@@ -241,21 +159,21 @@ int s5k4aa_init(struct sd *sd)
if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
u8 data = 0x02;
info("vertical flip quirk active");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
- s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
data |= S5K4AA_RM_V_FLIP;
data &= ~S5K4AA_RM_H_FLIP;
- s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
/* Decrement COLSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
data--;
- s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
/* Increment ROWSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
data++;
- s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
return (err < 0) ? err : 0;
@@ -272,20 +190,20 @@ int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
*val = data << 8;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
*val |= data;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -295,17 +213,17 @@ int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set exposure to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = (val >> 8) & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -314,16 +232,16 @@ int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_V_FLIP) >> 7;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -333,35 +251,35 @@ int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_V_FLIP)
| ((val & 0x01) << 7));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -370,15 +288,15 @@ int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_H_FLIP) >> 6;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -389,35 +307,35 @@ int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d",
val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -426,16 +344,16 @@ int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
*val = data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -445,28 +363,28 @@ int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set gain to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void s5k4aa_dump_registers(struct sd *sd)
+static void s5k4aa_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Dumping the s5k4aa register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
- s5k4aa_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -474,15 +392,15 @@ void s5k4aa_dump_registers(struct sd *sd)
info("s5k4aa register state dump complete");
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Probing for which registers that are "
"read/write for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_value, ctrl_value, test_value = 0xff;
- s5k4aa_read_sensor(sd, address, &old_value, 1);
- s5k4aa_write_sensor(sd, address, &test_value, 1);
- s5k4aa_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value)
info("register 0x%x is writeable", address);
@@ -490,9 +408,9 @@ void s5k4aa_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- s5k4aa_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
info("Read/write register probing complete");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
index 151c6f530..1f88b0d04 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -41,11 +41,10 @@
#define S5K4AA_WINDOW_HEIGHT_LO 0x09
#define S5K4AA_WINDOW_WIDTH_HI 0x0a
#define S5K4AA_WINDOW_WIDTH_LO 0x0b
-#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */
-#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost
- if too low, reduces frame rate
- if too high */
-#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */
+#define S5K4AA_GLOBAL_GAIN__ 0x0f
+/* sync lost, if too low, reduces frame rate if too high */
+#define S5K4AA_H_BLANK_HI__ 0x1d
+#define S5K4AA_H_BLANK_LO__ 0x1e
#define S5K4AA_EXPOSURE_HI 0x17
#define S5K4AA_EXPOSURE_LO 0x18
#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */
@@ -68,13 +67,6 @@ int s5k4aa_probe(struct sd *sd);
int s5k4aa_init(struct sd *sd);
int s5k4aa_power_down(struct sd *sd);
-void s5k4aa_dump_registers(struct sd *sd);
-
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -89,9 +81,8 @@ static struct m5602_sensor s5k4aa = {
.probe = s5k4aa_probe,
.init = s5k4aa_init,
.power_down = s5k4aa_power_down,
- .read_sensor = s5k4aa_read_sensor,
- .write_sensor = s5k4aa_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 4,
.ctrls = {
{
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 8988a728e..af3f2dc2c 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -18,6 +18,8 @@
#include "m5602_s5k83a.h"
+static void s5k83a_dump_registers(struct sd *sd);
+
int s5k83a_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0;
@@ -39,7 +41,7 @@ int s5k83a_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
if (preinit_s5k83a[i][0] == SENSOR)
- err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1],
+ err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
data, 2);
else
err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
@@ -49,10 +51,10 @@ int s5k83a_probe(struct sd *sd)
/* We don't know what register (if any) that contain the product id
* Just pick the first addresses that seem to produce the same results
* on multiple machines */
- if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
return -ENODEV;
- if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0xff) || (ver_id == 0xff))
@@ -68,91 +70,6 @@ sensor_found:
return 0;
}
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- if (err < 0)
- goto out;
- for (i = 0; i < len && !len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k83a_init(struct sd *sd)
{
int i, err = 0;
@@ -169,14 +86,14 @@ int s5k83a_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k83a[i][2];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k83a[i][2];
data[1] = init_s5k83a[i][3];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 2);
break;
default:
@@ -200,14 +117,14 @@ void s5k83a_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
- s5k83a_read_sensor(sd, address, &val, 1);
+ m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
@@ -215,15 +132,15 @@ void s5k83a_dump_registers(struct sd *sd)
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
- s5k83a_read_sensor(sd, address, &old_val, 1);
- s5k83a_write_sensor(sd, address, &test_val, 1);
- s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+ m5602_read_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &test_val, 1);
+ m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
info("register 0x%x is writeable", address);
@@ -231,11 +148,11 @@ void s5k83a_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original val */
- s5k83a_write_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &old_val, 1);
}
}
info("Read/write register probing complete");
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -244,11 +161,15 @@ int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ if (err < 0)
+ goto out;
+
data[1] = data[1] << 1;
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -259,23 +180,24 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0x00;
data[1] = 0x20;
- err = s5k83a_write_sensor(sd, 0x14, data, 2);
+ err = m5602_write_sensor(sd, 0x14, data, 2);
if (err < 0)
- return err;
+ goto out;
data[0] = 0x01;
data[1] = 0x00;
- err = s5k83a_write_sensor(sd, 0x0d, data, 2);
+ err = m5602_write_sensor(sd, 0x0d, data, 2);
if (err < 0)
- return err;
+ goto out;
/* FIXME: This is not sane, we need to figure out the composition
of these registers */
data[0] = val >> 3; /* brightness, high 5 bits */
data[1] = val >> 1; /* brightness, high 7 bits */
- err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -284,10 +206,14 @@ int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data;
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ if (err < 0)
+ goto out;
*val = data;
- return (err < 0) ? err : 0;
+
+out:
+ return err;
}
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
@@ -297,9 +223,9 @@ int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = val;
- err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -308,7 +234,9 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2);
+ if (err < 0)
+ goto out;
data[1] = data[1] & 0x3f;
if (data[1] > S5K83A_MAXIMUM_GAIN)
@@ -316,7 +244,8 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -327,9 +256,8 @@ int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0;
data[1] = val;
- err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+ return err;
}
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -339,14 +267,15 @@ int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x40) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -356,25 +285,26 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero six bit, seven is hflip */
data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
: (data[0] & 0x80) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0b : 0x0a;
- err = s5k83a_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -384,14 +314,15 @@ int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x80) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -401,23 +332,23 @@ int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero seven bit, six is vflip */
data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
: (data[0] & 0x40) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0a : 0x0b;
- err = s5k83a_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+out:
+ return err;
}
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h
index ee3ee9cfc..05ccb5b57 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -22,15 +22,15 @@
#include "m5602_sensor.h"
#define S5K83A_FLIP 0x01
-#define S5K83A_HFLIP_TUNE 0x03
-#define S5K83A_VFLIP_TUNE 0x05
-#define S5K83A_WHITENESS 0x0a
+#define S5K83A_HFLIP_TUNE 0x03
+#define S5K83A_VFLIP_TUNE 0x05
+#define S5K83A_WHITENESS 0x0a
#define S5K83A_GAIN 0x18
-#define S5K83A_BRIGHTNESS 0x1b
-#define S5K83A_PAGE_MAP 0xec
+#define S5K83A_BRIGHTNESS 0x1b
+#define S5K83A_PAGE_MAP 0xec
-#define S5K83A_DEFAULT_BRIGHTNESS 0x71
-#define S5K83A_DEFAULT_WHITENESS 0x7e
+#define S5K83A_DEFAULT_BRIGHTNESS 0x71
+#define S5K83A_DEFAULT_WHITENESS 0x7e
#define S5K83A_DEFAULT_GAIN 0x00
#define S5K83A_MAXIMUM_GAIN 0x3c
#define S5K83A_FLIP_MASK 0x10
@@ -46,13 +46,6 @@ int s5k83a_probe(struct sd *sd);
int s5k83a_init(struct sd *sd);
int s5k83a_power_down(struct sd *sd);
-void s5k83a_dump_registers(struct sd *sd);
-
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
@@ -64,15 +57,13 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
-
static struct m5602_sensor s5k83a = {
.name = "S5K83A",
.probe = s5k83a_probe,
.init = s5k83a_init,
.power_down = s5k83a_power_down,
- .read_sensor = s5k83a_read_sensor,
- .write_sensor = s5k83a_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 5,
.ctrls = {
{
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h
index 60c9a48e0..261623f0d 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h
+++ b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -49,23 +49,21 @@ struct m5602_sensor {
/* What i2c address the sensor is connected to */
u8 i2c_slave_id;
+ /* Width of each i2c register (in bytes) */
+ u8 i2c_regW;
+
/* Probes if the sensor is connected */
int (*probe)(struct sd *sd);
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
+ /* Executed when the camera starts to send data */
+ int (*start)(struct sd *sd);
+
/* Performs a power down sequence */
int (*power_down)(struct sd *sd);
- /* Reads a sensor register */
- int (*read_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
- /* Writes to a sensor register */
- int (*write_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int nctrls;
struct ctrl ctrls[M5602_MAX_CTRLS];
diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c
index 492cdd3b5..3d2090e67 100644
--- a/linux/drivers/media/video/gspca/mars.c
+++ b/linux/drivers/media/video/gspca/mars.c
@@ -39,7 +39,7 @@ struct sd {
static struct ctrl sd_ctrls[] = {
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 589,
diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c
index 5fb519da2..5e31a9145 100644
--- a/linux/drivers/media/video/gspca/ov519.c
+++ b/linux/drivers/media/video/gspca/ov519.c
@@ -164,7 +164,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -176,7 +176,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c
index 7332947cb..3bf15e401 100644
--- a/linux/drivers/media/video/gspca/ov534.c
+++ b/linux/drivers/media/video/gspca/ov534.c
@@ -55,7 +55,7 @@ struct sd {
static struct ctrl sd_ctrls[] = {
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
.bytesperline = 640 * 2,
.sizeimage = 640 * 480 * 2,
@@ -63,65 +63,66 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static void ov534_reg_write(struct usb_device *udev, u16 reg, u8 val)
+static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
{
- u8 data = val;
+ struct usb_device *udev = gspca_dev->dev;
int ret;
PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
+ gspca_dev->usb_buf[0] = val;
ret = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
0x1,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0, reg, &data, 1, CTRL_TIMEOUT);
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
if (ret < 0)
PDEBUG(D_ERR, "write failed");
}
-static u8 ov534_reg_read(struct usb_device *udev, u16 reg)
+static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
{
- u8 data;
+ struct usb_device *udev = gspca_dev->dev;
int ret;
ret = usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
0x1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0, reg, &data, 1, CTRL_TIMEOUT);
- PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, data);
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
if (ret < 0)
PDEBUG(D_ERR, "read failed");
- return data;
+ return gspca_dev->usb_buf[0];
}
/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
* (direction and output)? */
-static void ov534_set_led(struct usb_device *udev, int status)
+static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
{
u8 data;
PDEBUG(D_CONF, "led status: %d", status);
- data = ov534_reg_read(udev, 0x21);
+ data = ov534_reg_read(gspca_dev, 0x21);
data |= 0x80;
- ov534_reg_write(udev, 0x21, data);
+ ov534_reg_write(gspca_dev, 0x21, data);
- data = ov534_reg_read(udev, 0x23);
+ data = ov534_reg_read(gspca_dev, 0x23);
if (status)
data |= 0x80;
else
data &= ~(0x80);
- ov534_reg_write(udev, 0x23, data);
+ ov534_reg_write(gspca_dev, 0x23, data);
}
-static int sccb_check_status(struct usb_device *udev)
+static int sccb_check_status(struct gspca_dev *gspca_dev)
{
u8 data;
int i;
for (i = 0; i < 5; i++) {
- data = ov534_reg_read(udev, OV534_REG_STATUS);
+ data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
switch (data) {
case 0x00:
@@ -131,37 +132,37 @@ static int sccb_check_status(struct usb_device *udev)
case 0x03:
break;
default:
- PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5\n",
+ PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
data, i + 1);
}
}
return 0;
}
-static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val)
+static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
{
PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val);
- ov534_reg_write(udev, OV534_REG_SUBADDR, reg);
- ov534_reg_write(udev, OV534_REG_WRITE, val);
- ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
- if (!sccb_check_status(udev))
+ if (!sccb_check_status(gspca_dev))
PDEBUG(D_ERR, "sccb_reg_write failed");
}
#ifdef GSPCA_DEBUG
-static u8 sccb_reg_read(struct usb_device *udev, u16 reg)
+static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
{
- ov534_reg_write(udev, OV534_REG_SUBADDR, reg);
- ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
- if (!sccb_check_status(udev))
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
PDEBUG(D_ERR, "sccb_reg_read failed 1");
- ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_READ_2);
- if (!sccb_check_status(udev))
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
PDEBUG(D_ERR, "sccb_reg_read failed 2");
- return ov534_reg_read(udev, OV534_REG_READ);
+ return ov534_reg_read(gspca_dev, OV534_REG_READ);
}
#endif
@@ -318,26 +319,26 @@ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
switch (fr) {
case 50:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x01);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
- ov534_reg_write(gspca_dev->dev, 0xe5, 0x02);
+ sccb_reg_write(gspca_dev, 0x11, 0x01);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
break;
case 40:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x02);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1);
- ov534_reg_write(gspca_dev->dev, 0xe5, 0x04);
+ sccb_reg_write(gspca_dev, 0x11, 0x02);
+ sccb_reg_write(gspca_dev, 0x0d, 0xc1);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
break;
/* case 30: */
default:
fr = 30;
- sccb_reg_write(gspca_dev->dev, 0x11, 0x04);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x81);
- ov534_reg_write(gspca_dev->dev, 0xe5, 0x02);
+ sccb_reg_write(gspca_dev, 0x11, 0x04);
+ sccb_reg_write(gspca_dev, 0x0d, 0x81);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
break;
case 15:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x03);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
- ov534_reg_write(gspca_dev->dev, 0xe5, 0x04);
+ sccb_reg_write(gspca_dev, 0x11, 0x03);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
break;
}
@@ -346,27 +347,28 @@ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
}
/* setup method */
-static void ov534_setup(struct usb_device *udev)
+static void ov534_setup(struct gspca_dev *gspca_dev)
{
int i;
/* Initialize bridge chip */
for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++)
- ov534_reg_write(udev, ov534_reg_initdata[i][0],
+ ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0],
ov534_reg_initdata[i][1]);
PDEBUG(D_PROBE, "sensor is ov%02x%02x",
- sccb_reg_read(udev, 0x0a), sccb_reg_read(udev, 0x0b));
+ sccb_reg_read(gspca_dev, 0x0a),
+ sccb_reg_read(gspca_dev, 0x0b));
- ov534_set_led(udev, 1);
+ ov534_set_led(gspca_dev, 1);
/* Initialize sensor */
for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++)
- sccb_reg_write(udev, ov772x_reg_initdata[i][0],
+ sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0],
ov772x_reg_initdata[i][1]);
- ov534_reg_write(udev, 0xe0, 0x09);
- ov534_set_led(udev, 0);
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
}
/* this function is called at probe time */
@@ -390,7 +392,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- ov534_setup(gspca_dev->dev);
+ ov534_setup(gspca_dev);
ov534_set_frame_rate(gspca_dev);
return 0;
@@ -399,8 +401,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
static int sd_start(struct gspca_dev *gspca_dev)
{
/* start streaming data */
- ov534_set_led(gspca_dev->dev, 1);
- ov534_reg_write(gspca_dev->dev, 0xe0, 0x00);
+ ov534_set_led(gspca_dev, 1);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
return 0;
}
@@ -408,8 +410,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
/* stop streaming data */
- ov534_reg_write(gspca_dev->dev, 0xe0, 0x09);
- ov534_set_led(gspca_dev->dev, 0);
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
}
/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
@@ -499,8 +501,8 @@ discard:
}
/* get stream parameters (framerate) */
-int sd_get_streamparm(struct gspca_dev *gspca_dev,
- struct v4l2_streamparm *parm)
+static int sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
{
struct v4l2_captureparm *cp = &parm->parm.capture;
struct v4l2_fract *tpf = &cp->timeperframe;
@@ -517,8 +519,8 @@ int sd_get_streamparm(struct gspca_dev *gspca_dev,
}
/* set stream parameters (framerate) */
-int sd_set_streamparm(struct gspca_dev *gspca_dev,
- struct v4l2_streamparm *parm)
+static int sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
{
struct v4l2_captureparm *cp = &parm->parm.capture;
struct v4l2_fract *tpf = &cp->timeperframe;
diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c
index 39473e6b9..c90ac852b 100644
--- a/linux/drivers/media/video/gspca/pac207.c
+++ b/linux/drivers/media/video/gspca/pac207.c
@@ -1,7 +1,7 @@
/*
* Pixart PAC207BCA library
*
- * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
*
@@ -27,7 +27,7 @@
#include "gspca.h"
-MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
MODULE_DESCRIPTION("Pixart PAC207");
MODULE_LICENSE("GPL");
@@ -149,7 +149,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = (176 + 2) * 144,
@@ -529,6 +529,7 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x041e, 0x4028)},
{USB_DEVICE(0x093a, 0x2460)},
+ {USB_DEVICE(0x093a, 0x2461)},
{USB_DEVICE(0x093a, 0x2463)},
{USB_DEVICE(0x093a, 0x2464)},
{USB_DEVICE(0x093a, 0x2468)},
diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c
index b0a9d0687..83ab0df0e 100644
--- a/linux/drivers/media/video/gspca/pac7311.c
+++ b/linux/drivers/media/video/gspca/pac7311.c
@@ -226,7 +226,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 8 + 590,
@@ -1078,11 +1078,13 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
+ {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c
index 5ca8ab780..75b44b362 100644
--- a/linux/drivers/media/video/gspca/sonixb.c
+++ b/linux/drivers/media/video/gspca/sonixb.c
@@ -227,7 +227,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -249,7 +249,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c
index ff94cd639..217db0d88 100644
--- a/linux/drivers/media/video/gspca/sonixj.c
+++ b/linux/drivers/media/video/gspca/sonixj.c
@@ -225,7 +225,7 @@ static __u32 ctrl_dis[] = {
/* SENSOR_OV7660 6 */
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c
index 2e1c5e50e..6b4865280 100644
--- a/linux/drivers/media/video/gspca/spca500.c
+++ b/linux/drivers/media/video/gspca/spca500.c
@@ -111,7 +111,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -124,7 +124,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c
index 17dab9859..609459321 100644
--- a/linux/drivers/media/video/gspca/spca501.c
+++ b/linux/drivers/media/video/gspca/spca501.c
@@ -132,7 +132,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c
index c52598e94..854ed1ff5 100644
--- a/linux/drivers/media/video/gspca/spca505.c
+++ b/linux/drivers/media/video/gspca/spca505.c
@@ -59,7 +59,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c
index 645ee9d44..96e2512e0 100644
--- a/linux/drivers/media/video/gspca/spca506.c
+++ b/linux/drivers/media/video/gspca/spca506.c
@@ -110,7 +110,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c
index c9360cdb8..698d4ed95 100644
--- a/linux/drivers/media/video/gspca/spca508.c
+++ b/linux/drivers/media/video/gspca/spca508.c
@@ -62,7 +62,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c
index 4c0046cc7..499511fa0 100644
--- a/linux/drivers/media/video/gspca/spca561.c
+++ b/linux/drivers/media/video/gspca/spca561.c
@@ -32,22 +32,22 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u16 contrast; /* rev72a only */
-#define CONTRAST_MIN 0x0000
-#define CONTRAST_DEF 0x2000
-#define CONTRAST_MAX 0x3fff
-
__u16 exposure; /* rev12a only */
#define EXPOSURE_MIN 1
#define EXPOSURE_DEF 200
#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */
+ __u8 contrast; /* rev72a only */
+#define CONTRAST_MIN 0x00
+#define CONTRAST_DEF 0x20
+#define CONTRAST_MAX 0x3f
+
__u8 brightness; /* rev72a only */
#define BRIGHTNESS_MIN 0
-#define BRIGHTNESS_DEF 32
-#define BRIGHTNESS_MAX 63
+#define BRIGHTNESS_DEF 0x20
+#define BRIGHTNESS_MAX 0x3f
- __u8 white; /* rev12a only */
+ __u8 white;
#define WHITE_MIN 1
#define WHITE_DEF 0x40
#define WHITE_MAX 0x7f
@@ -73,7 +73,7 @@ struct sd {
#define AG_CNT_START 13
};
-static struct v4l2_pix_format sif_012a_mode[] = {
+static const struct v4l2_pix_format sif_012a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -96,7 +96,7 @@ static struct v4l2_pix_format sif_012a_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_072a_mode[] = {
+static const struct v4l2_pix_format sif_072a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -146,98 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = {
#define SPCA561_SNAPBIT 0x20
#define SPCA561_SNAPCTRL 0x40
-static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
-{
- int ret;
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 500);
- PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
- if (ret < 0)
- PDEBUG(D_ERR, "reg write: error %d", ret);
-}
-
-static void write_vector(struct gspca_dev *gspca_dev,
- const __u16 data[][2])
-{
- struct usb_device *dev = gspca_dev->dev;
- int i;
-
- i = 0;
- while (data[i][1] != 0) {
- reg_w_val(dev, data[i][1], data[i][0]);
- i++;
- }
-}
-
-/* read 'len' bytes to gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev,
- __u16 index, __u16 length)
-{
- usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, length, 500);
-}
-
-static void reg_w_buf(struct gspca_dev *gspca_dev,
- __u16 index, const __u8 *buffer, __u16 len)
-{
- memcpy(gspca_dev->usb_buf, buffer, len);
- usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, len, 500);
-}
-
-static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg)
-{
- int retry = 60;
- __u8 DataLow;
- __u8 DataHight;
-
- DataLow = valeur;
- DataHight = valeur >> 8;
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8805, DataLow);
- reg_w_val(gspca_dev->dev, 0x8800, DataHight);
- while (retry--) {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- }
-}
-
-static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
-{
- int retry = 60;
- __u8 value;
- __u8 vallsb;
-
- reg_w_val(gspca_dev->dev, 0x8804, 0x92);
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01));
- do {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- } while (--retry);
- if (retry == 0)
- return -1;
- reg_r(gspca_dev, 0x8800, 1);
- value = gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8805, 1);
- vallsb = gspca_dev->usb_buf[0];
- return ((int) value << 8) | vallsb;
-}
-
-static const __u16 spca561_init_data[][2] = {
+static const __u16 rev72a_init_data1[][2] = {
{0x0000, 0x8114}, /* Software GPIO output data */
{0x0001, 0x8114}, /* Software GPIO output data */
{0x0000, 0x8112}, /* Some kind of reset */
@@ -247,44 +156,26 @@ static const __u16 spca561_init_data[][2] = {
{0x0001, 0x8118}, /* Conf sensor */
{0x0092, 0x8804}, /* I know nothing about these */
{0x0010, 0x8802}, /* 0x88xx registers, so I won't */
- /***************/
{0x000d, 0x8805}, /* sensor default setting */
- {0x0001, 0x8801}, /* 1 <- 0x0d */
- {0x0000, 0x8800},
- {0x0018, 0x8805},
- {0x0002, 0x8801}, /* 2 <- 0x18 */
- {0x0000, 0x8800},
- {0x0065, 0x8805},
- {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */
- {0x0001, 0x8800},
- {0x0021, 0x8805},
- {0x0005, 0x8801}, /* 5 <- 0x21 */
- {0x0000, 0x8800},
- {0x00aa, 0x8805},
- {0x0007, 0x8801}, /* 7 <- 0xaa */
- {0x0000, 0x8800},
- {0x0004, 0x8805},
- {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */
- {0x0015, 0x8800},
- {0x0002, 0x8805},
- {0x0039, 0x8801}, /* 0x39 <- 0x02 */
- {0x0000, 0x8800},
- {0x0010, 0x8805},
- {0x0035, 0x8801}, /* 0x35 <- 0x10 */
- {0x0000, 0x8800},
- {0x0049, 0x8805},
- {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */
- {0x0010, 0x8800},
- {0x000b, 0x8805},
- {0x0028, 0x8801}, /* 0x28 <- 0x0b */
- {0x0000, 0x8800},
- {0x000f, 0x8805},
- {0x003b, 0x8801}, /* 0x3b <- 0x0f */
- {0x0000, 0x8800},
- {0x0000, 0x8805},
- {0x003c, 0x8801}, /* 0x3c <- 0x00 */
- {0x0000, 0x8800},
- /***************/
+ {}
+};
+static const __u16 rev72a_init_sensor1[][2] = {
+ /* ms-win values */
+ {0x0001, 0x0018}, /* 0x01 <- 0x0d */
+ {0x0002, 0x0065}, /* 0x02 <- 0x18 */
+ {0x0004, 0x0121}, /* 0x04 <- 0x0165 */
+ {0x0005, 0x00aa}, /* 0x05 <- 0x21 */
+ {0x0007, 0x0004}, /* 0x07 <- 0xaa */
+ {0x0020, 0x1502}, /* 0x20 <- 0x1504 */
+ {0x0039, 0x0010}, /* 0x39 <- 0x02 */
+ {0x0035, 0x0049}, /* 0x35 <- 0x10 */
+ {0x0009, 0x100b}, /* 0x09 <- 0x1049 */
+ {0x0028, 0x000f}, /* 0x28 <- 0x0b */
+ {0x003b, 0x003c}, /* 0x3b <- 0x0f */
+ {0x003c, 0x0000}, /* 0x3c <- 0x00 */
+ {}
+};
+static const __u16 rev72a_init_data2[][2] = {
{0x0018, 0x8601}, /* Pixel/line selection for color separation */
{0x0000, 0x8602}, /* Optical black level for user setting */
{0x0060, 0x8604}, /* Optical black horizontal offset */
@@ -309,10 +200,18 @@ static const __u16 spca561_init_data[][2] = {
{0x0004, 0x8612}, /* Gr offset for white balance */
{0x0007, 0x8613}, /* B offset for white balance */
{0x0000, 0x8614}, /* Gb offset for white balance */
+#if 1
+/* from ms-win */
+ {0x0035, 0x8651}, /* R gain for white balance */
+ {0x0040, 0x8652}, /* Gr gain for white balance */
+ {0x005f, 0x8653}, /* B gain for white balance */
+ {0x0040, 0x8654}, /* Gb gain for white balance */
+#else
{0x008c, 0x8651}, /* R gain for white balance */
{0x008c, 0x8652}, /* Gr gain for white balance */
{0x00b5, 0x8653}, /* B gain for white balance */
{0x008c, 0x8654}, /* Gb gain for white balance */
+#endif
{0x0002, 0x8502}, /* Maximum average bit rate stuff */
{0x0011, 0x8802},
@@ -324,29 +223,22 @@ static const __u16 spca561_init_data[][2] = {
{0x0002, 0x865b}, /* Horizontal offset for valid pixels */
{0x0003, 0x865c}, /* Vertical offset for valid lines */
- /***************//* sensor active */
- {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */
- {0x0021, 0x8805},
- {0x0001, 0x8800},
- {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */
- {0x0065, 0x8805},
- {0x0001, 0x8800},
- {0x0005, 0x8801}, /* 0x05 <- 0x2f */
- {0x002f, 0x8805},
- {0x0000, 0x8800},
- {0x0006, 0x8801}, /* 0x06 <- 0 */
- {0x0000, 0x8805},
- {0x0000, 0x8800},
- {0x000a, 0x8801}, /* 0x0a <- 2 */
- {0x0002, 0x8805},
- {0x0000, 0x8800},
- {0x0009, 0x8801}, /* 0x09 <- 0x1061 */
- {0x0061, 0x8805},
- {0x0010, 0x8800},
- {0x0035, 0x8801}, /* 0x35 <-0x14 */
- {0x0014, 0x8805},
- {0x0000, 0x8800},
+ {}
+};
+static const __u16 rev72a_init_sensor2[][2] = {
+ /* ms-win values */
+ {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */
+ {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */
+ {0x0005, 0x002f}, /* 0x05 <- 0x2f */
+ {0x0006, 0x0000}, /* 0x06 <- 0 */
+ {0x000a, 0x0002}, /* 0x0a <- 2 */
+ {0x0009, 0x1061}, /* 0x09 <- 0x1061 */
+ {0x0035, 0x0014}, /* 0x35 <- 0x14 */
+ {}
+};
+static const __u16 rev72a_init_data3[][2] = {
{0x0030, 0x8112}, /* ISO and drop packet enable */
+/*fixme: should stop here*/
{0x0000, 0x8112}, /* Some kind of reset ???? */
{0x0009, 0x8118}, /* Enable sensor and set standby */
{0x0000, 0x8114}, /* Software GPIO output data */
@@ -434,21 +326,6 @@ static const __u16 spca561_init_data[][2] = {
{}
};
-#if 0
-static void sensor_reset(struct gspca_dev *gspca_dev)
-{
- reg_w_val(gspca_dev->dev, 0x8631, 0xc8);
- reg_w_val(gspca_dev->dev, 0x8634, 0xc8);
- reg_w_val(gspca_dev->dev, 0x8112, 0x00);
- reg_w_val(gspca_dev->dev, 0x8114, 0x00);
- reg_w_val(gspca_dev->dev, 0x8118, 0x21);
- reg_w_val(gspca_dev->dev, 0x8804, 0x92); /* i2c init */
- reg_w_val(gspca_dev->dev, 0x8802, 0x14);
- i2c_write(gspca_dev, 0x0001, 0x0d);
- i2c_write(gspca_dev, 0x0000, 0x0d);
-}
-#endif
-
/******************** QC Express etch2 stuff ********************/
static const __u16 Pb100_1map8300[][2] = {
/* reg, value */
@@ -529,22 +406,112 @@ static const __u16 spca561_161rev12A_data2[][2] = {
{}
};
-static void sensor_mapwrite(struct gspca_dev *gspca_dev,
- const __u16 sensormap[][2])
+static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg write: error %d", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][2])
{
- int i = 0;
- __u8 usbval[2];
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
- while (sensormap[i][0]) {
- usbval[0] = sensormap[i][1];
- usbval[1] = sensormap[i][1] >> 8;
- reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2);
+ i = 0;
+ while (data[i][1] != 0) {
+ reg_w_val(dev, data[i][1], data[i][0]);
i++;
}
}
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length, 500);
+}
+
+/* write 'len' bytes from gspca_dev->usb_buf */
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 len)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len, 500);
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+{
+ int retry = 60;
+
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8805, value);
+ reg_w_val(gspca_dev->dev, 0x8800, value >> 8);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0])
+ return;
+ } while (--retry);
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+ int retry = 60;
+ __u8 value;
+
+ reg_w_val(gspca_dev->dev, 0x8804, 0x92);
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0]) {
+ reg_r(gspca_dev, 0x8800, 1);
+ value = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8805, 1);
+ return ((int) value << 8) | gspca_dev->usb_buf[0];
+ }
+ } while (--retry);
+ return -1;
+}
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+ const __u16 (*sensormap)[2])
+{
+ while ((*sensormap)[0]) {
+ gspca_dev->usb_buf[0] = (*sensormap)[1];
+ gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
+ reg_w_buf(gspca_dev, (*sensormap)[0], 2);
+ sensormap++;
+ }
+}
+
+static void write_sensor_72a(struct gspca_dev *gspca_dev,
+ const __u16 (*sensor)[2])
+{
+ while ((*sensor)[0]) {
+ i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
+ sensor++;
+ }
+}
+
static void init_161rev12A(struct gspca_dev *gspca_dev)
{
-/* sensor_reset(gspca_dev); (not in win) */
write_vector(gspca_dev, spca561_161rev12A_data1);
sensor_mapwrite(gspca_dev, Pb100_1map8300);
/*fixme: should be in sd_start*/
@@ -612,49 +579,68 @@ static int sd_init_12a(struct gspca_dev *gspca_dev)
static int sd_init_72a(struct gspca_dev *gspca_dev)
{
PDEBUG(D_STREAM, "Chip revision: 072a");
- write_vector(gspca_dev, spca561_init_data);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+ write_vector(gspca_dev, rev72a_init_data2);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ write_vector(gspca_dev, rev72a_init_data3);
return 0;
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+/* rev 72a only */
+static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- __u8 lowb;
+ __u8 value;
- switch (sd->chip_revision) {
- case Rev072A:
- lowb = sd->contrast >> 8;
- reg_w_val(dev, 0x8651, lowb);
- reg_w_val(dev, 0x8652, lowb);
- reg_w_val(dev, 0x8653, lowb);
- reg_w_val(dev, 0x8654, lowb);
- break;
- default: {
-/* case Rev012A: { */
- static const __u8 Reg8391[] =
- { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 };
+ value = sd->brightness;
- reg_w_buf(gspca_dev, 0x8391, Reg8391, 8);
- reg_w_buf(gspca_dev, 0x8390, Reg8391, 8);
- break;
- }
- }
+ /* offsets for white balance */
+ reg_w_val(dev, 0x8611, value); /* R */
+ reg_w_val(dev, 0x8612, value); /* Gr */
+ reg_w_val(dev, 0x8613, value); /* B */
+ reg_w_val(dev, 0x8614, value); /* Gb */
}
-/* rev12a only */
static void setwhite(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u16 white;
- __u8 reg8614, reg8616;
+ __u8 blue, red;
+ __u16 reg;
- white = sd->white;
/* try to emulate MS-win as possible */
- reg8616 = 0x90 - white * 5 / 8;
- reg_w_val(gspca_dev->dev, 0x8616, reg8616);
- reg8614 = 0x20 + white * 3 / 8;
- reg_w_val(gspca_dev->dev, 0x8614, reg8614);
+ white = sd->white;
+ red = 0x20 + white * 3 / 8;
+ blue = 0x90 - white * 5 / 8;
+ if (sd->chip_revision == Rev012A) {
+ reg = 0x8614;
+ } else {
+ reg = 0x8651;
+ red += sd->contrast - 0x20;
+ blue += sd->contrast - 0x20;
+ }
+ reg_w_val(gspca_dev->dev, reg, red);
+ reg_w_val(gspca_dev->dev, reg + 2, blue);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ __u8 value;
+
+ if (sd->chip_revision != Rev072A)
+ return;
+ value = sd->contrast + 0x20;
+
+ /* gains for white balance */
+ setwhite(gspca_dev);
+/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
+ reg_w_val(dev, 0x8652, value); /* Gr */
+/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
+ reg_w_val(dev, 0x8654, value); /* Gb */
}
/* rev 12a only */
@@ -663,7 +649,6 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int expo;
int clock_divider;
- __u8 data[2];
/* Register 0x8309 controls exposure for the spca561,
the basic exposure setting goes from 1-2047, where 1 is completely
@@ -687,20 +672,19 @@ static void setexposure(struct gspca_dev *gspca_dev)
clock_divider = 3;
}
expo |= clock_divider << 11;
- data[0] = expo;
- data[1] = expo >> 8;
- reg_w_buf(gspca_dev, 0x8309, data, 2);
+ gspca_dev->usb_buf[0] = expo;
+ gspca_dev->usb_buf[1] = expo >> 8;
+ reg_w_buf(gspca_dev, 0x8309, 2);
}
/* rev 12a only */
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 data[2];
- data[0] = sd->gain;
- data[1] = 0;
- reg_w_buf(gspca_dev, 0x8335, data, 2);
+ gspca_dev->usb_buf[0] = sd->gain;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8335, 2);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -716,9 +700,9 @@ static void setautogain(struct gspca_dev *gspca_dev)
static int sd_start_12a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
- int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */
- __u8 Reg8307[] = { 0xaa, 0x00 };
int mode;
+ static const __u8 Reg8391[8] =
+ {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode <= 1) {
@@ -730,14 +714,21 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
* is sufficient to push raw frames at ~20fps */
reg_w_val(dev, 0x8500, mode);
} /* -- qq@kuku.eu.org */
- reg_w_buf(gspca_dev, 0x8307, Reg8307, 2);
- reg_w_val(gspca_dev->dev, 0x8700, Clck);
+
+ gspca_dev->usb_buf[0] = 0xaa;
+ gspca_dev->usb_buf[1] = 0x00;
+ reg_w_buf(gspca_dev, 0x8307, 2);
+ /* clock - lower 0x8X values lead to fps > 30 */
+ reg_w_val(gspca_dev->dev, 0x8700, 0x8a);
/* 0x8f 0x85 0x27 clock */
reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
reg_w_val(gspca_dev->dev, 0x850b, 0x03);
- setcontrast(gspca_dev);
+ memcpy(gspca_dev->usb_buf, Reg8391, 8);
+ reg_w_buf(gspca_dev, 0x8391, 8);
+ reg_w_buf(gspca_dev, 0x8390, 8);
setwhite(gspca_dev);
setautogain(gspca_dev);
+/* setgain(gspca_dev); */
setexposure(gspca_dev);
return 0;
}
@@ -764,6 +755,9 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
reg_w_val(dev, 0x8500, mode); /* mode */
reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
reg_w_val(dev, 0x8112, 0x10 | 0x20);
+ setcontrast(gspca_dev);
+/* setbrightness(gspca_dev); * fixme: bad values */
+ setwhite(gspca_dev);
setautogain(gspca_dev);
return 0;
}
@@ -805,7 +799,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
__u8 luma_mean = 110;
__u8 luma_delta = 20;
__u8 spring = 4;
- __u8 reg8339[2];
if (sd->ag_cnt < 0)
return;
@@ -848,13 +841,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (gainG > 0x3f)
gainG = 0x3f;
- else if (gainG < 4)
+ else if (gainG < 3)
gainG = 3;
i2c_write(gspca_dev, gainG, 0x35);
- if (expotimes >= 0x0256)
+ if (expotimes > 0x0256)
expotimes = 0x0256;
- else if (expotimes < 4)
+ else if (expotimes < 3)
expotimes = 3;
i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
}
@@ -862,13 +855,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
case Rev012A:
reg_r(gspca_dev, 0x8330, 2);
if (gspca_dev->usb_buf[1] > 0x08) {
- reg8339[0] = ++sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = ++sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
} else if (gspca_dev->usb_buf[1] < 0x02) {
- reg8339[0] = --sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = --sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
}
break;
}
@@ -881,8 +874,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (data[0]) {
- case 0: /* start of frame */
+ switch (data[0]) { /* sequence number */
+ case 0: /* start of frame */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
data += SPCA561_OFFSET_DATA;
@@ -904,8 +897,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
frame, data, len);
}
return;
- case 0xff: /* drop */
-/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ case 0xff: /* drop (empty mpackets) */
return;
}
data++;
@@ -914,55 +906,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
/* rev 72a only */
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 value;
-
- value = sd->brightness;
- reg_w_val(gspca_dev->dev, 0x8611, value);
- reg_w_val(gspca_dev->dev, 0x8612, value);
- reg_w_val(gspca_dev->dev, 0x8613, value);
- reg_w_val(gspca_dev->dev, 0x8614, value);
-}
-
-static void getbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8611, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8612, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8613, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8614, 1);
- tot += gspca_dev->usb_buf[0];
- sd->brightness = tot >> 2;
-}
-
-/* rev72a only */
-static void getcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8651, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8652, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8653, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8654, 1);
- tot += gspca_dev->usb_buf[0];
- sd->contrast = tot << 6;
- PDEBUG(D_CONF, "get contrast %d", sd->contrast);
-}
-
-/* rev 72a only */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -977,7 +920,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getbrightness(gspca_dev);
*val = sd->brightness;
return 0;
}
@@ -997,7 +939,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getcontrast(gspca_dev);
*val = sd->contrast;
return 0;
}
@@ -1020,7 +961,6 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-/* rev12a only */
static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1135,6 +1075,19 @@ static struct ctrl sd_ctrls_12a[] = {
static struct ctrl sd_ctrls_72a[] = {
{
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = WHITE_MIN,
+ .maximum = WHITE_MAX,
+ .step = 1,
+ .default_value = WHITE_DEF,
+ },
+ .set = sd_setwhite,
+ .get = sd_getwhite,
+ },
+ {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c
index 74f57db53..60de9af87 100644
--- a/linux/drivers/media/video/gspca/stk014.c
+++ b/linux/drivers/media/video/gspca/stk014.c
@@ -109,7 +109,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/stv06xx/Kconfig b/linux/drivers/media/video/gspca/stv06xx/Kconfig
new file mode 100644
index 000000000..634ad38d9
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/Kconfig
@@ -0,0 +1,9 @@
+config USB_STV06XX
+ tristate "STV06XX USB Camera Driver"
+ depends on USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on
+ the ST STV06XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv06xx.
diff --git a/linux/drivers/media/video/gspca/stv06xx/Makefile b/linux/drivers/media/video/gspca/stv06xx/Makefile
new file mode 100644
index 000000000..8f002b623
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+ stv06xx_vv6410.o \
+ stv06xx_hdcs.o \
+ stv06xx_pb0100.o
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c
new file mode 100644
index 000000000..29e43718b
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_sensor.h"
+
+MODULE_AUTHOR("Erik Andrén");
+MODULE_DESCRIPTION("STV06XX USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+int dump_bridge;
+int dump_sensor;
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+ u8 len = (i2c_data > 0xff) ? 2 : 1;
+
+ buf[0] = i2c_data & 0xff;
+ buf[1] = (i2c_data >> 8) & 0xff;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, address, 0, buf, len,
+ STV06XX_URB_MSG_TIMEOUT);
+
+
+ PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
+ i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, address, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+
+ *i2c_data = buf[0];
+
+ PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d",
+ *i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Wraps the normal write sensor bytes / words functions for writing a
+ single value */
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
+{
+ if (sd->sensor->i2c_len == 2) {
+ u16 data[2] = { address, value };
+ return stv06xx_write_sensor_words(sd, data, 1);
+ } else {
+ u8 data[2] = { address, value };
+ return stv06xx_write_sensor_bytes(sd, data, 1);
+ }
+}
+
+static int stv06xx_write_sensor_finish(struct sd *sd)
+{
+ int err = 0;
+
+ if (IS_850(sd)) {
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* Quickam Web needs an extra packet */
+ buf[0] = 0;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1704, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j] = data[2*i+1];
+ PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j * 2] = data[2*i+1];
+ buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
+ PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
+ if (err < 0)
+ return err;
+
+ /* Clear mem */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+
+ buf[0] = address;
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = 0;
+
+ /* Read I2C register */
+ buf[0x22] = I2C_READ_CMD;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0) {
+ PDEBUG(D_ERR, "I2C Read: error writing address: %d", err);
+ return err;
+ }
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (sd->sensor->i2c_len == 2)
+ *value = buf[0] | (buf[1] << 8);
+ else
+ *value = buf[0];
+
+ PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d",
+ *value, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Dumps all bridge registers */
+static void stv06xx_dump_bridge(struct sd *sd)
+{
+ int i;
+ u8 data, buf;
+
+ info("Dumping all stv06xx bridge registers");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+
+ info("Read 0x%x from address 0x%x", data, i);
+ }
+
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+ buf = data;
+
+ stv06xx_write_bridge(sd, i, 0xff);
+ stv06xx_read_bridge(sd, i, &data);
+ if (data == 0xff)
+ info("Register 0x%x is read/write", i);
+ else if (data != buf)
+ info("Register 0x%x is read/write,"
+ "but only partially", i);
+ else
+ info("Register 0x%x is read-only", i);
+
+ stv06xx_write_bridge(sd, i, buf);
+ }
+}
+
+/* this function is called at probe and resume time */
+static int stv06xx_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(D_PROBE, "Initializing camera");
+
+ /* Let the usb init settle for a bit
+ before performing the initialization */
+ msleep(250);
+
+ err = sd->sensor->init(sd);
+
+ if (dump_sensor)
+ sd->sensor->dump(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Start the camera */
+static int stv06xx_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ /* Prepare the sensor for start */
+ err = sd->sensor->start(sd);
+ if (err < 0)
+ goto out;
+
+ /* Start isochronous streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Starting stream failed");
+ else
+ PDEBUG(D_STREAM, "Started streaming");
+
+ return (err < 0) ? err : 0;
+}
+
+static void stv06xx_stopN(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* stop ISO-streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
+ if (err < 0)
+ goto out;
+
+ err = sd->sensor->stop(sd);
+ if (err < 0)
+ goto out;
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Failed to stop stream");
+ else
+ PDEBUG(D_STREAM, "Stopped streaming");
+}
+
+/*
+ * Analyse an USB packet of the data stream and store it appropriately.
+ * Each packet contains an integral number of chunks. Each chunk has
+ * 2-bytes identification, followed by 2-bytes that describe the chunk
+ * length. Known/guessed chunk identifications are:
+ * 8001/8005/C001/C005 - Begin new frame
+ * 8002/8006/C002/C006 - End frame
+ * 0200/4200 - Contains actual image data, bayer or compressed
+ * 0005 - 11 bytes of unknown data
+ * 0100 - 2 bytes of unknown data
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ PDEBUG(D_PACK, "Packet of length %d arrived", len);
+
+ /* A packet may contain several frames
+ loop until the whole packet is reached */
+ while (len) {
+ int id, chunk_len;
+
+ if (len < 4) {
+ PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
+ return;
+ }
+
+ /* Capture the id */
+ id = (data[0] << 8) | data[1];
+
+ /* Capture the chunk length */
+ chunk_len = (data[2] << 8) | data[3];
+ PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
+
+ data += 4;
+ len -= 4;
+
+ if (len < chunk_len) {
+ PDEBUG(D_ERR, "URB packet length is smaller"
+ " than the specified chunk length");
+ return;
+ }
+
+ switch (id) {
+ case 0x0200:
+ case 0x4200:
+ PDEBUG(D_PACK, "Frame data packet detected");
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, chunk_len);
+ break;
+
+ case 0x8001:
+ case 0x8005:
+ case 0xc001:
+ case 0xc005:
+ PDEBUG(D_PACK, "Starting new frame");
+
+ /* Create a new frame, chunk length should be zero */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a SOF");
+ break;
+
+ case 0x8002:
+ case 0x8006:
+ case 0xc002:
+ PDEBUG(D_PACK, "End of frame detected");
+
+ /* Complete the last frame (if any) */
+ gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a EOF");
+ break;
+
+ case 0x0005:
+ PDEBUG(D_PACK, "Chunk 0x005 detected");
+ /* Unknown chunk with 11 bytes of data,
+ occurs just before end of each frame
+ in compressed mode */
+ break;
+
+ case 0x0100:
+ PDEBUG(D_PACK, "Chunk 0x0100 detected");
+ /* Unknown chunk with 2 bytes of data,
+ occurs 2-3 times per USB interrupt */
+ break;
+ default:
+ PDEBUG(D_PACK, "Unknown chunk %d detected", id);
+ /* Unknown chunk */
+ }
+ data += chunk_len;
+ len -= chunk_len;
+ }
+}
+
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = stv06xx_config,
+ .init = stv06xx_init,
+ .start = stv06xx_start,
+ .stopN = stv06xx_stopN,
+ .pkt_scan = stv06xx_pkt_scan
+};
+
+/* This function is called at probe time */
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ PDEBUG(D_PROBE, "Configuring camera");
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = STV_ISOC_ENDPOINT_ADDR;
+ sd->desc = sd_desc;
+ gspca_dev->sd_desc = &sd->desc;
+
+ if (dump_bridge)
+ stv06xx_dump_bridge(sd);
+
+ sd->sensor = &stv06xx_sensor_vv6410;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1x00;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1020;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_pb0100;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */
+ {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */
+ {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ PDEBUG(D_PROBE, "Probing for a stv06xx device");
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+void sd_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+ PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
+
+ if (sd->sensor->disconnect)
+ sd->sensor->disconnect(sd);
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx.h
new file mode 100644
index 000000000..1207e7d17
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_H_
+#define STV06XX_H_
+
+#include "gspca.h"
+
+#define MODULE_NAME "STV06xx"
+
+#define STV_ISOC_ENDPOINT_ADDR 0x81
+
+#ifndef V4L2_PIX_FMT_SGRBG8
+#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G')
+#endif
+
+#define STV_REG23 0x0423
+
+/* Control registers of the STV0600 ASIC */
+#define STV_I2C_PARTNER 0x1420
+#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421
+#define STV_I2C_READ_WRITE_TOGGLE 0x1422
+#define STV_I2C_FLUSH 0x1423
+#define STV_I2C_SUCC_READ_REG_VALS 0x1424
+
+#define STV_ISO_ENABLE 0x1440
+#define STV_SCAN_RATE 0x1443
+#define STV_LED_CTRL 0x1445
+#define STV_STV0600_EMULATION 0x1446
+#define STV_REG00 0x1500
+#define STV_REG01 0x1501
+#define STV_REG02 0x1502
+#define STV_REG03 0x1503
+#define STV_REG04 0x1504
+
+#define STV_ISO_SIZE_L 0x15c1
+#define STV_ISO_SIZE_H 0x15c2
+
+/* Refers to the CIF 352x288 and QCIF 176x144 */
+/* 1: 288 lines, 2: 144 lines */
+#define STV_Y_CTRL 0x15c3
+
+/* 0xa: 352 columns, 0x6: 176 columns */
+#define STV_X_CTRL 0x1680
+
+#define STV06XX_URB_MSG_TIMEOUT 5000
+
+#define I2C_MAX_BYTES 16
+#define I2C_MAX_WORDS 8
+
+#define I2C_BUFFER_LENGTH 0x23
+#define I2C_READ_CMD 3
+#define I2C_WRITE_CMD 1
+
+#define LED_ON 1
+#define LED_OFF 0
+
+/* STV06xx device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* A pointer to the currently connected sensor */
+ const struct stv06xx_sensor *sensor;
+
+ /* A pointer to the sd_desc struct */
+ struct sd_desc desc;
+
+ /* Sensor private data */
+ void *sensor_priv;
+};
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data);
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data);
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len);
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len);
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value);
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value);
+
+#endif
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
new file mode 100644
index 000000000..1cfe58504
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_hdcs.h"
+
+enum hdcs_power_state {
+ HDCS_STATE_SLEEP,
+ HDCS_STATE_IDLE,
+ HDCS_STATE_RUN
+};
+
+/* no lock? */
+struct hdcs {
+ enum hdcs_power_state state;
+ int w, h;
+
+ /* visible area of the sensor array */
+ struct {
+ int left, top;
+ int width, height;
+ int border;
+ } array;
+
+ struct {
+ /* Column timing overhead */
+ u8 cto;
+ /* Column processing overhead */
+ u8 cpo;
+ /* Row sample period constant */
+ u16 rs;
+ /* Exposure reset duration */
+ u16 er;
+ } exp;
+
+ int psmp;
+};
+
+static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
+{
+ u8 regs[I2C_MAX_BYTES * 2];
+ int i;
+
+ if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) ||
+ (reg + len > 0xff)))
+ return -EINVAL;
+
+ for (i = 0; i < len; i++, reg++) {
+ regs[2*i] = reg;
+ regs[2*i+1] = vals[i];
+ }
+
+ return stv06xx_write_sensor_bytes(sd, regs, len);
+}
+
+static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 val;
+ int ret;
+
+ if (hdcs->state == state)
+ return 0;
+
+ /* we need to go idle before running or sleeping */
+ if (hdcs->state != HDCS_STATE_IDLE) {
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (ret)
+ return ret;
+ }
+
+ hdcs->state = HDCS_STATE_IDLE;
+
+ if (state == HDCS_STATE_IDLE)
+ return 0;
+
+ switch (state) {
+ case HDCS_STATE_SLEEP:
+ val = HDCS_SLEEP_MODE;
+ break;
+
+ case HDCS_STATE_RUN:
+ val = HDCS_RUN_ENABLE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val);
+ if (ret < 0)
+ hdcs->state = state;
+
+ return ret;
+}
+
+static int hdcs_reset(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (err < 0)
+ hdcs->state = HDCS_STATE_IDLE;
+
+ return err;
+}
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ int cycles;
+ int err;
+ int rowexp;
+ u16 data[2];
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]);
+ if (err < 0)
+ return err;
+
+ rowexp = (data[1] << 8) | data[0];
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+ rp = hdcs->exp.rs + cp;
+
+ cycles = rp * rowexp;
+ *val = cycles / HDCS_CLK_FREQ_MHZ;
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+ return 0;
+}
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+ int rowexp, srowexp;
+ int max_srowexp;
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ /* Minimum number of column timing periods
+ within the column processing period */
+ int mnct;
+ int cycles, err;
+ u8 exp[4];
+
+ cycles = val * HDCS_CLK_FREQ_MHZ;
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+
+ /* the cycles one row takes */
+ rp = hdcs->exp.rs + cp;
+
+ rowexp = cycles / rp;
+
+ /* the remaining cycles */
+ cycles -= rowexp * rp;
+
+ /* calculate sub-row exposure */
+ if (IS_1020(sd)) {
+ /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
+ srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
+
+ mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
+ max_srowexp = hdcs->w - mnct;
+ } else {
+ /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
+ srowexp = cp - hdcs->exp.er - 6 - cycles;
+
+ mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
+ max_srowexp = cp - mnct * ct - 1;
+ }
+
+ if (srowexp < 0)
+ srowexp = 0;
+ else if (srowexp > max_srowexp)
+ srowexp = max_srowexp;
+
+ if (IS_1020(sd)) {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = (srowexp >> 2) & 0xff;
+ /* this clears exposure error flag */
+ exp[3] = 0x1;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ } else {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = srowexp & 0xff;
+ exp[3] = srowexp >> 8;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ if (err < 0)
+ return err;
+
+ /* clear exposure error flag */
+ err = stv06xx_write_sensor(sd,
+ HDCS_STATUS, BIT(4));
+ }
+ PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d",
+ val, rowexp, srowexp);
+ return err;
+}
+
+static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b)
+{
+ u8 gains[4];
+
+ /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
+ if (r > 127)
+ r = 0x80 | (r / 2);
+ if (g > 127)
+ g = 0x80 | (g / 2);
+ if (b > 127)
+ b = 0x80 | (b / 2);
+
+ gains[0] = g;
+ gains[1] = r;
+ gains[2] = b;
+ gains[3] = g;
+
+ return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
+}
+
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+ u16 data;
+
+ err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data);
+
+ /* Bit 7 doubles the gain */
+ if (data & 0x80)
+ *val = (data & 0x7f) * 2;
+ else
+ *val = data;
+
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return err;
+}
+
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ PDEBUG(D_V4L2, "Writing gain %d", val);
+ return hdcs_set_gains((struct sd *) gspca_dev,
+ val & 0xff, val & 0xff, val & 0xff);
+}
+
+static int hdcs_set_size(struct sd *sd,
+ unsigned int width, unsigned int height)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 win[4];
+ unsigned int x, y;
+ int err;
+
+ /* must be multiple of 4 */
+ width = (width + 3) & ~0x3;
+ height = (height + 3) & ~0x3;
+
+ if (width > hdcs->array.width)
+ width = hdcs->array.width;
+
+ if (IS_1020(sd)) {
+ /* the borders are also invalid */
+ if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
+ > hdcs->array.height)
+ height = hdcs->array.height - 2 * hdcs->array.border -
+ HDCS_1020_BOTTOM_Y_SKIP;
+
+ y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+ + hdcs->array.top;
+ } else if (height > hdcs->array.height) {
+ height = hdcs->array.height;
+ y = hdcs->array.top + (hdcs->array.height - height) / 2;
+ }
+
+ x = hdcs->array.left + (hdcs->array.width - width) / 2;
+
+ win[0] = y / 4;
+ win[1] = x / 4;
+ win[2] = (y + height) / 4 - 1;
+ win[3] = (x + width) / 4 - 1;
+
+ err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
+ if (err < 0)
+ return err;
+
+ /* Update the current width and height */
+ hdcs->w = width;
+ hdcs->h = height;
+ return err;
+}
+
+static int hdcs_probe_1x00(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x08)
+ return -ENODEV;
+
+ info("HDCS-1000/1100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ hdcs->array.left = 8;
+ hdcs->array.top = 8;
+ hdcs->array.width = HDCS_1X00_DEF_WIDTH;
+ hdcs->array.height = HDCS_1X00_DEF_HEIGHT;
+ hdcs->array.border = 4;
+
+ hdcs->exp.cto = 4;
+ hdcs->exp.cpo = 2;
+ hdcs->exp.rs = 186;
+ hdcs->exp.er = 100;
+
+ /*
+ * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP:
+ * 4 = doesn't work at all
+ * 5 = 7.8 fps,
+ * 6 = 6.9 fps,
+ * 8 = 6.3 fps,
+ * 10 = 5.5 fps,
+ * 15 = 4.4 fps,
+ * 31 = 2.8 fps
+ *
+ * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP:
+ * 15 = doesn't work at all
+ * 18 = doesn't work at all
+ * 19 = 7.3 fps
+ * 20 = 7.4 fps
+ * 21 = 7.4 fps
+ * 22 = 7.4 fps
+ * 24 = 6.3 fps
+ * 30 = 5.4 fps
+ */
+ hdcs->psmp = IS_870(sd) ? 20 : 5;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_probe_1020(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x10)
+ return -ENODEV;
+
+ info("HDCS-1020 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ /*
+ * From Andrey's test image: looks like HDCS-1020 upper-left
+ * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
+ * visible pixel at 375,299 (x maybe even larger?)
+ */
+ hdcs->array.left = 24;
+ hdcs->array.top = 4;
+ hdcs->array.width = HDCS_1020_DEF_WIDTH;
+ hdcs->array.height = 304;
+ hdcs->array.border = 4;
+
+ hdcs->psmp = 6;
+
+ hdcs->exp.cto = 3;
+ hdcs->exp.cpo = 3;
+ hdcs->exp.rs = 155;
+ hdcs->exp.er = 96;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_start(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_RUN);
+}
+
+static int hdcs_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_SLEEP);
+}
+
+static void hdcs_disconnect(struct sd *sd)
+{
+ PDEBUG(D_PROBE, "Disconnecting the sensor");
+ kfree(sd->sensor_priv);
+}
+
+static int hdcs_init(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int i, err = 0;
+
+ /* Set the STV0602AA in STV0600 emulation mode */
+ if (IS_870(sd))
+ stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1);
+
+ /* Execute the bridge init */
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) {
+ err = stv06xx_write_bridge(sd, stv_bridge_init[i][0],
+ stv_bridge_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* sensor soft reset */
+ hdcs_reset(sd);
+
+ /* Execute the sensor init */
+ for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) {
+ err = stv06xx_write_sensor(sd, stv_sensor_init[i][0],
+ stv_sensor_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* Enable continous frame capture, bit 2: stop when frame complete */
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3));
+ if (err < 0)
+ return err;
+
+ /* Set PGA sample duration
+ (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */
+ if (IS_1020(sd))
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp);
+ else
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN,
+ HDCS_DEFAULT_GAIN);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
+ return err;
+}
+
+static int hdcs_dump(struct sd *sd)
+{
+ u16 reg, val;
+
+ info("Dumping sensor registers:");
+
+ for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
+ stv06xx_read_sensor(sd, reg, &val);
+ info("reg 0x%02x = 0x%02x", reg, val);
+ }
+ return 0;
+}
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
new file mode 100644
index 000000000..9c7279a4c
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_HDCS_H_
+#define STV06XX_HDCS_H_
+
+#include "stv06xx_sensor.h"
+
+#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
+#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
+
+#define HDCS_1X00_DEF_WIDTH 360
+#define HDCS_1X00_DEF_HEIGHT 296
+
+#define HDCS_1020_DEF_WIDTH 352
+#define HDCS_1020_DEF_HEIGHT 292
+
+#define HDCS_1020_BOTTOM_Y_SKIP 4
+
+#define HDCS_CLK_FREQ_MHZ 25
+
+#define HDCS_ADC_START_SIG_DUR 3
+
+/* LSB bit of I2C or register address signifies write (0) or read (1) */
+/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
+/* Identifications Register */
+#define HDCS_IDENT (0x00 << 1)
+/* Status Register */
+#define HDCS_STATUS (0x01 << 1)
+/* Interrupt Mask Register */
+#define HDCS_IMASK (0x02 << 1)
+/* Pad Control Register */
+#define HDCS_PCTRL (0x03 << 1)
+/* Pad Drive Control Register */
+#define HDCS_PDRV (0x04 << 1)
+/* Interface Control Register */
+#define HDCS_ICTRL (0x05 << 1)
+/* Interface Timing Register */
+#define HDCS_ITMG (0x06 << 1)
+/* Baud Fraction Register */
+#define HDCS_BFRAC (0x07 << 1)
+/* Baud Rate Register */
+#define HDCS_BRATE (0x08 << 1)
+/* ADC Control Register */
+#define HDCS_ADCCTRL (0x09 << 1)
+/* First Window Row Register */
+#define HDCS_FWROW (0x0a << 1)
+/* First Window Column Register */
+#define HDCS_FWCOL (0x0b << 1)
+/* Last Window Row Register */
+#define HDCS_LWROW (0x0c << 1)
+/* Last Window Column Register */
+#define HDCS_LWCOL (0x0d << 1)
+/* Timing Control Register */
+#define HDCS_TCTRL (0x0e << 1)
+/* PGA Gain Register: Even Row, Even Column */
+#define HDCS_ERECPGA (0x0f << 1)
+/* PGA Gain Register: Even Row, Odd Column */
+#define HDCS_EROCPGA (0x10 << 1)
+/* PGA Gain Register: Odd Row, Even Column */
+#define HDCS_ORECPGA (0x11 << 1)
+/* PGA Gain Register: Odd Row, Odd Column */
+#define HDCS_OROCPGA (0x12 << 1)
+/* Row Exposure Low Register */
+#define HDCS_ROWEXPL (0x13 << 1)
+/* Row Exposure High Register */
+#define HDCS_ROWEXPH (0x14 << 1)
+
+/* I2C Registers only for HDCS-1000/1100 */
+/* Sub-Row Exposure Low Register */
+#define HDCS00_SROWEXPL (0x15 << 1)
+/* Sub-Row Exposure High Register */
+#define HDCS00_SROWEXPH (0x16 << 1)
+/* Configuration Register */
+#define HDCS00_CONFIG (0x17 << 1)
+/* Control Register */
+#define HDCS00_CONTROL (0x18 << 1)
+
+/* I2C Registers only for HDCS-1020 */
+/* Sub-Row Exposure Register */
+#define HDCS20_SROWEXP (0x15 << 1)
+/* Error Control Register */
+#define HDCS20_ERROR (0x16 << 1)
+/* Interface Timing 2 Register */
+#define HDCS20_ITMG2 (0x17 << 1)
+/* Interface Control 2 Register */
+#define HDCS20_ICTRL2 (0x18 << 1)
+/* Horizontal Blank Register */
+#define HDCS20_HBLANK (0x19 << 1)
+/* Vertical Blank Register */
+#define HDCS20_VBLANK (0x1a << 1)
+/* Configuration Register */
+#define HDCS20_CONFIG (0x1b << 1)
+/* Control Register */
+#define HDCS20_CONTROL (0x1c << 1)
+
+#define HDCS_RUN_ENABLE (1 << 2)
+#define HDCS_SLEEP_MODE (1 << 1)
+
+#define HDCS_DEFAULT_EXPOSURE 5000
+#define HDCS_DEFAULT_GAIN 128
+
+static int hdcs_probe_1x00(struct sd *sd);
+static int hdcs_probe_1020(struct sd *sd);
+static int hdcs_start(struct sd *sd);
+static int hdcs_init(struct sd *sd);
+static int hdcs_stop(struct sd *sd);
+static int hdcs_dump(struct sd *sd);
+static void hdcs_disconnect(struct sd *sd);
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+ .name = "HP HDCS-1000/1100",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1x00,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .disconnect = hdcs_disconnect,
+ .dump = hdcs_dump,
+
+ .nctrls = 2,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_exposure,
+ .get = hdcs_get_exposure
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_gain,
+ .get = hdcs_get_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1X00_DEF_WIDTH,
+ HDCS_1X00_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+ .bytesperline = HDCS_1X00_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+ .name = "HDCS-1020",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .nctrls = 0,
+ .ctrls = {},
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1020,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .dump = hdcs_dump,
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1020_DEF_WIDTH,
+ HDCS_1020_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+ .bytesperline = HDCS_1020_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const u16 stv_bridge_init[][2] = {
+ {STV_ISO_ENABLE, 0},
+ {STV_REG23, 0},
+ {STV_REG00, 0x1d},
+ {STV_REG01, 0xb5},
+ {STV_REG02, 0xa8},
+ {STV_REG03, 0x95},
+ {STV_REG04, 0x07},
+
+ {STV_SCAN_RATE, 0x20},
+ {STV_ISO_SIZE_L, 847},
+ {STV_Y_CTRL, 0x01},
+ {STV_X_CTRL, 0x0a}
+};
+
+static const u8 stv_sensor_init[][2] = {
+ /* Clear status (writing 1 will clear the corresponding status bit) */
+ {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ /* Disable all interrupts */
+ {HDCS_IMASK, 0x00},
+ {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)},
+ {HDCS_PDRV, 0x00},
+ {HDCS_ICTRL, BIT(5)},
+ {HDCS_ITMG, BIT(4) | BIT(1)},
+ /* ADC output resolution to 10 bits */
+ {HDCS_ADCCTRL, 10}
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
new file mode 100644
index 000000000..d0a0f8596
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+/*
+ * The spec file for the PB-0100 suggests the following for best quality
+ * images after the sensor has been reset :
+ *
+ * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC
+ to produce good black level
+ * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes
+ through R53
+ * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for
+ auto-exposure
+ * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain
+ * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value
+ * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on
+ auto-exposure routine
+ * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
+ */
+
+#include "stv06xx_pb0100.h"
+
+static int pb0100_probe(struct sd *sd)
+{
+ u16 sensor;
+ int i, err;
+ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if ((sensor >> 8) == 0x64) {
+ sensor_settings = kmalloc(
+ stv06xx_sensor_pb0100.nctrls * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ info("Photobit pb0100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls;
+ for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++)
+ sensor_settings[i] = stv06xx_sensor_pb0100.
+ ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int pb0100_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+ u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ /* Setup sensor window */
+ if (mode & PB0100_CROP_TO_VGA) {
+ stv06xx_write_sensor(sd, PB_RSTART, 30);
+ stv06xx_write_sensor(sd, PB_CSTART, 20);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);
+ } else {
+ stv06xx_write_sensor(sd, PB_RSTART, 8);
+ stv06xx_write_sensor(sd, PB_CSTART, 4);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);
+ }
+
+ if (mode & PB0100_SUBSAMPLE) {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ /* larger -> slower */
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* set_gain also sets red and blue balance */
+ pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);
+ pb0100_set_autogain_target(&sd->gspca_dev,
+ sensor_settings[AUTOGAIN_TARGET_IDX]);
+ pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
+ PDEBUG(D_STREAM, "Started stream, status: %d", err);
+
+ return (err < 0) ? err : 0;
+}
+
+static int pb0100_stop(struct sd *sd)
+{
+ int err;
+
+ err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
+
+ if (err < 0)
+ goto out;
+
+ /* Set bit 1 to zero */
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ PDEBUG(D_STREAM, "Halting stream");
+out:
+ return (err < 0) ? err : 0;
+}
+
+/* FIXME: Sort the init commands out and put them into tables,
+ this is only for getting the camera to work */
+/* FIXME: No error handling for now,
+ add this once the init has been converted to proper tables */
+static int pb0100_init(struct sd *sd)
+{
+ stv06xx_write_bridge(sd, STV_REG00, 1);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);
+
+ /* Reset sensor */
+ stv06xx_write_sensor(sd, PB_RESET, 1);
+ stv06xx_write_sensor(sd, PB_RESET, 0);
+
+ /* Disable chip */
+ stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ /* Gain stuff...*/
+ stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));
+ stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);
+
+ /* Set up auto-exposure */
+ /* ADC VREF_HI new setting for a transition
+ from the Expose1 to the Expose2 setting */
+ stv06xx_write_sensor(sd, PB_R28, 12);
+ /* gain max for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);
+ /* gain min for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);
+ /* Maximum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R54, 3);
+ /* Minimum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R55, 0);
+ stv06xx_write_sensor(sd, PB_UPDATEINT, 1);
+ /* R15 Expose0 (maximum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R15, 800);
+ /* R17 Expose2 (minimum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R17, 10);
+
+ stv06xx_write_sensor(sd, PB_EXPGAIN, 0);
+
+ /* 0x14 */
+ stv06xx_write_sensor(sd, PB_VOFFSET, 0);
+ /* 0x0D */
+ stv06xx_write_sensor(sd, PB_ADCGAINH, 11);
+ /* Set black level (important!) */
+ stv06xx_write_sensor(sd, PB_ADCGAINL, 0);
+
+ /* ??? */
+ stv06xx_write_bridge(sd, STV_REG00, 0x11);
+ stv06xx_write_bridge(sd, STV_REG03, 0x45);
+ stv06xx_write_bridge(sd, STV_REG04, 0x07);
+
+ /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */
+ stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847);
+
+ /* Scan/timing for the sensor */
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+ stv06xx_write_sensor(sd, PB_CFILLIN, 14);
+ stv06xx_write_sensor(sd, PB_VBL, 0);
+ stv06xx_write_sensor(sd, PB_FINTTIME, 0);
+ stv06xx_write_sensor(sd, PB_RINTTIME, 123);
+
+ stv06xx_write_bridge(sd, STV_REG01, 0xc2);
+ stv06xx_write_bridge(sd, STV_REG02, 0xb0);
+ return 0;
+}
+
+static int pb0100_dump(struct sd *sd)
+{
+ return 0;
+}
+
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[GAIN_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
+ PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);
+
+ if (!err)
+ err = pb0100_set_red_balance(gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (!err)
+ err = pb0100_set_blue_balance(gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+
+ return err;
+}
+
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_RGAIN, val);
+ PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_BGAIN, val);
+ PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
+ PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_IDX] = val;
+ if (sensor_settings[AUTOGAIN_IDX]) {
+ if (sensor_settings[NATURAL_IDX])
+ val = BIT(6)|BIT(4)|BIT(0);
+ else
+ val = BIT(4)|BIT(0);
+ } else
+ val = 0;
+
+ err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
+ PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",
+ sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],
+ err);
+
+ return err;
+}
+
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_TARGET_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, totalpixels, brightpixels, darkpixels;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_TARGET_IDX] = val;
+
+ /* Number of pixels counted by the sensor when subsampling the pixels.
+ * Slightly larger than the real value to avoid oscillation */
+ totalpixels = gspca_dev->width * gspca_dev->height;
+ totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
+
+ brightpixels = (totalpixels * val) >> 8;
+ darkpixels = totalpixels - brightpixels;
+ err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
+
+ PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[NATURAL_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[NATURAL_IDX] = val;
+
+ return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+}
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
new file mode 100644
index 000000000..4b47270aa
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_PB0100_H_
+#define STV06XX_PB0100_H_
+
+#include "stv06xx_sensor.h"
+
+/* mode priv field flags */
+#define PB0100_CROP_TO_VGA 0x01
+#define PB0100_SUBSAMPLE 0x02
+
+/* I2C Registers */
+#define PB_IDENT 0x00 /* Chip Version */
+#define PB_RSTART 0x01 /* Row Window Start */
+#define PB_CSTART 0x02 /* Column Window Start */
+#define PB_RWSIZE 0x03 /* Row Window Size */
+#define PB_CWSIZE 0x04 /* Column Window Size */
+#define PB_CFILLIN 0x05 /* Column Fill-In */
+#define PB_VBL 0x06 /* Vertical Blank Count */
+#define PB_CONTROL 0x07 /* Control Mode */
+#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */
+#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */
+#define PB_ROWSPEED 0x0a /* Row Speed Control */
+#define PB_ABORTFRAME 0x0b /* Abort Frame */
+#define PB_R12 0x0c /* Reserved */
+#define PB_RESET 0x0d /* Reset */
+#define PB_EXPGAIN 0x0e /* Exposure Gain Command */
+#define PB_R15 0x0f /* Expose0 */
+#define PB_R16 0x10 /* Expose1 */
+#define PB_R17 0x11 /* Expose2 */
+#define PB_R18 0x12 /* Low0_DAC */
+#define PB_R19 0x13 /* Low1_DAC */
+#define PB_R20 0x14 /* Low2_DAC */
+#define PB_R21 0x15 /* Threshold11 */
+#define PB_R22 0x16 /* Threshold0x */
+#define PB_UPDATEINT 0x17 /* Update Interval */
+#define PB_R24 0x18 /* High_DAC */
+#define PB_R25 0x19 /* Trans0H */
+#define PB_R26 0x1a /* Trans1L */
+#define PB_R27 0x1b /* Trans1H */
+#define PB_R28 0x1c /* Trans2L */
+#define PB_R29 0x1d /* Reserved */
+#define PB_R30 0x1e /* Reserved */
+#define PB_R31 0x1f /* Wait to Read */
+#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */
+#define PB_R33 0x21 /* IREF_VLN */
+#define PB_R34 0x22 /* IREF_VLP */
+#define PB_R35 0x23 /* IREF_VLN_INTEG */
+#define PB_R36 0x24 /* IREF_MASTER */
+#define PB_R37 0x25 /* IDACP */
+#define PB_R38 0x26 /* IDACN */
+#define PB_R39 0x27 /* DAC_Control_Reg */
+#define PB_R40 0x28 /* VCL */
+#define PB_R41 0x29 /* IREF_VLN_ADCIN */
+#define PB_R42 0x2a /* Reserved */
+#define PB_G1GAIN 0x2b /* Green 1 Gain */
+#define PB_BGAIN 0x2c /* Blue Gain */
+#define PB_RGAIN 0x2d /* Red Gain */
+#define PB_G2GAIN 0x2e /* Green 2 Gain */
+#define PB_R47 0x2f /* Dark Row Address */
+#define PB_R48 0x30 /* Dark Row Options */
+#define PB_R49 0x31 /* Reserved */
+#define PB_R50 0x32 /* Image Test Data */
+#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */
+#define PB_ADCMINGAIN 0x34 /* Minimum Gain */
+#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */
+#define PB_R54 0x36 /* Maximum Frame */
+#define PB_R55 0x37 /* Minimum Frame */
+#define PB_R56 0x38 /* Reserved */
+#define PB_VOFFSET 0x39 /* VOFFSET */
+#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */
+#define PB_ADCGAINH 0x3b /* VREF_HI */
+#define PB_ADCGAINL 0x3c /* VREF_LO */
+#define PB_R61 0x3d /* Reserved */
+#define PB_R62 0x3e /* Reserved */
+#define PB_R63 0x3f /* Reserved */
+#define PB_R64 0x40 /* Red/Blue Gain */
+#define PB_R65 0x41 /* Green 2/Green 1 Gain */
+#define PB_R66 0x42 /* VREF_HI/LO */
+#define PB_R67 0x43 /* Integration Time/Row Unit Count */
+#define PB_R240 0xf0 /* ADC Test */
+#define PB_R241 0xf1 /* Chip Enable */
+#define PB_R242 0xf2 /* Reserved */
+
+static int pb0100_probe(struct sd *sd);
+static int pb0100_start(struct sd *sd);
+static int pb0100_init(struct sd *sd);
+static int pb0100_stop(struct sd *sd);
+static int pb0100_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+ .name = "PB-0100",
+ .i2c_flush = 1,
+ .i2c_addr = 0xba,
+ .i2c_len = 2,
+
+ .nctrls = 7,
+ .ctrls = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_gain,
+ .get = pb0100_get_gain
+ },
+#define RED_BALANCE_IDX 1
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_red_balance,
+ .get = pb0100_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_blue_balance,
+ .get = pb0100_get_blue_balance
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 511,
+ .step = 1,
+ .default_value = 12
+ },
+ .set = pb0100_set_exposure,
+ .get = pb0100_get_exposure
+ },
+#define AUTOGAIN_IDX 4
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_autogain,
+ .get = pb0100_get_autogain
+ },
+#define AUTOGAIN_TARGET_IDX 5
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain Target",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_autogain_target,
+ .get = pb0100_get_autogain_target
+ },
+#define NATURAL_IDX 6
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Natural Light Source",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_natural,
+ .get = pb0100_get_natural
+ },
+ },
+
+ .init = pb0100_init,
+ .probe = pb0100_probe,
+ .start = pb0100_start,
+ .stop = pb0100_stop,
+ .dump = pb0100_dump,
+
+ .nmodes = 2,
+ .modes = {
+/* low res / subsample modes disabled as they are only half res horizontal,
+ halving the vertical resolution does not seem to work */
+#if 0
+ {
+ 160,
+ 120,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 160 * 120 * 2,
+ .bytesperline = 160,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_CROP_TO_VGA | PB0100_SUBSAMPLE
+ },
+ {
+ 176,
+ 144,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 176 * 144 * 2,
+ .bytesperline = 176,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_SUBSAMPLE
+ },
+#endif
+ {
+ 320,
+ 240,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_CROP_TO_VGA
+ },
+ {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ }
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
new file mode 100644
index 000000000..c726dacef
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_SENSOR_H_
+#define STV06XX_SENSOR_H_
+
+#include "stv06xx.h"
+
+#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850)
+#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870)
+#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020)
+
+extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+
+#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+struct stv06xx_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* Sensor i2c address */
+ u8 i2c_addr;
+
+ /* Flush value*/
+ u8 i2c_flush;
+
+ /* length of an i2c word */
+ u8 i2c_len;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Executed at device disconnect */
+ void (*disconnect)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Instructs the sensor to start streaming */
+ int (*start)(struct sd *sd);
+
+ /* Instructs the sensor to stop streaming */
+ int (*stop)(struct sd *sd);
+
+ /* Instructs the sensor to dump all its contents */
+ int (*dump)(struct sd *sd);
+
+ int nctrls;
+ struct ctrl ctrls[STV06XX_MAX_CTRLS];
+
+ char nmodes;
+ struct v4l2_pix_format modes[];
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
new file mode 100644
index 000000000..1ca91f2a6
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_vv6410.h"
+
+static int vv6410_probe(struct sd *sd)
+{
+ u16 data;
+ int err;
+
+ err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if (data == 0x19) {
+ info("vv6410 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int vv6410_init(struct sd *sd)
+{
+ int err = 0, i;
+
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
+ /* if NULL then len contains single value */
+ if (stv_bridge_init[i].data == NULL) {
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start,
+ stv_bridge_init[i].len);
+ } else {
+ int j;
+ for (j = 0; j < stv_bridge_init[i].len; j++)
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start + j,
+ stv_bridge_init[i].data[j]);
+ }
+ }
+
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
+ ARRAY_SIZE(vv6410_sensor_init));
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ if (priv & VV6410_CROP_TO_QVGA) {
+ PDEBUG(D_CONF, "Cropping to QVGA");
+ stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
+ } else {
+ stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
+ }
+
+ if (priv & VV6410_SUBSAMPLE) {
+ PDEBUG(D_CONF, "Enabling subsampling");
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* Turn on LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return 0;
+}
+
+static int vv6410_stop(struct sd *sd)
+{
+ int err;
+
+ /* Turn off LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_dump(struct sd *sd)
+{
+ u8 i;
+ int err = 0;
+
+ info("Dumping all vv6410 sensor registers");
+ for (i = 0; i < 0xff && !err; i++) {
+ u16 data;
+ err = stv06xx_read_sensor(sd, i, &data);
+ info("Register 0x%x contained 0x%x", i, data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_HFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_HFLIP;
+ else
+ i2c_data &= ~VV6410_HFLIP;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_VFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_VFLIP;
+ else
+ i2c_data &= ~VV6410_VFLIP;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data);
+
+ *val = i2c_data & 0xf;
+
+ PDEBUG(D_V4L2, "Read analog gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_V4L2, "Set analog gain to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+ return (err < 0) ? err : 0;
+}
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
new file mode 100644
index 000000000..3ff8c4ea3
--- /dev/null
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_VV6410_H_
+#define STV06XX_VV6410_H_
+
+#include "stv06xx_sensor.h"
+
+#define VV6410_COLS 416
+#define VV6410_ROWS 320
+
+/* Status registers */
+/* Chip identification number including revision indicator */
+#define VV6410_DEVICEH 0x00
+#define VV6410_DEVICEL 0x01
+
+/* User can determine whether timed I2C data
+ has been consumed by interrogating flag states */
+#define VV6410_STATUS0 0x02
+
+/* Current line counter value */
+#define VV6410_LINECOUNTH 0x03
+#define VV6410_LINECOUNTL 0x04
+
+/* End x coordinate of image size */
+#define VV6410_XENDH 0x05
+#define VV6410_XENDL 0x06
+
+/* End y coordinate of image size */
+#define VV6410_YENDH 0x07
+#define VV6410_YENDL 0x08
+
+/* This is the average pixel value returned from the
+ dark line offset cancellation algorithm */
+#define VV6410_DARKAVGH 0x09
+#define VV6410_DARKAVGL 0x0a
+
+/* This is the average pixel value returned from the
+ black line offset cancellation algorithm */
+#define VV6410_BLACKAVGH 0x0b
+#define VV6410_BLACKAVGL 0x0c
+
+/* Flags to indicate whether the x or y image coordinates have been clipped */
+#define VV6410_STATUS1 0x0d
+
+/* Setup registers */
+
+/* Low-power/sleep modes & video timing */
+#define VV6410_SETUP0 0x10
+
+/* Various parameters */
+#define VV6410_SETUP1 0x11
+
+/* Contains pixel counter reset value used by external sync */
+#define VV6410_SYNCVALUE 0x12
+
+/* Frame grabbing modes (FST, LST and QCK) */
+#define VV6410_FGMODES 0x14
+
+/* FST and QCK mapping modes. */
+#define VV6410_PINMAPPING 0x15
+
+/* Data resolution */
+#define VV6410_DATAFORMAT 0x16
+
+/* Output coding formats */
+#define VV6410_OPFORMAT 0x17
+
+/* Various mode select bits */
+#define VV6410_MODESELECT 0x18
+
+/* Exposure registers */
+/* Fine exposure. */
+#define VV6410_FINEH 0x20
+#define VV6410_FINEL 0x21
+
+/* Coarse exposure */
+#define VV6410_COARSEH 0x22
+#define VV6410_COARSEL 0x23
+
+/* Analog gain setting */
+#define VV6410_ANALOGGAIN 0x24
+
+/* Clock division */
+#define VV6410_CLKDIV 0x25
+
+/* Dark line offset cancellation value */
+#define VV6410_DARKOFFSETH 0x2c
+#define VV6410_DARKOFFSETL 0x2d
+
+/* Dark line offset cancellation enable */
+#define VV6410_DARKOFFSETSETUP 0x2e
+
+/* Video timing registers */
+/* Line Length (Pixel Clocks) */
+#define VV6410_LINELENGTHH 0x52
+#define VV6410_LINELENGTHL 0x53
+
+/* X-co-ordinate of top left corner of region of interest (x-offset) */
+#define VV6410_XOFFSETH 0x57
+#define VV6410_XOFFSETL 0x58
+
+/* Y-coordinate of top left corner of region of interest (y-offset) */
+#define VV6410_YOFFSETH 0x59
+#define VV6410_YOFFSETL 0x5a
+
+/* Field length (Lines) */
+#define VV6410_FIELDLENGTHH 0x61
+#define VV6410_FIELDLENGTHL 0x62
+
+/* System registers */
+/* Black offset cancellation default value */
+#define VV6410_BLACKOFFSETH 0x70
+#define VV6410_BLACKOFFSETL 0x71
+
+/* Black offset cancellation setup */
+#define VV6410_BLACKOFFSETSETUP 0x72
+
+/* Analog Control Register 0 */
+#define VV6410_CR0 0x75
+
+/* Analog Control Register 1 */
+#define VV6410_CR1 0x76
+
+/* ADC Setup Register */
+#define VV6410_AS0 0x77
+
+/* Analog Test Register */
+#define VV6410_AT0 0x78
+
+/* Audio Amplifier Setup Register */
+#define VV6410_AT1 0x79
+
+#define VV6410_HFLIP (1 << 3)
+#define VV6410_VFLIP (1 << 4)
+
+#define VV6410_LOW_POWER_MODE (1 << 0)
+#define VV6410_SOFT_RESET (1 << 2)
+#define VV6410_PAL_25_FPS (0 << 3)
+
+#define VV6410_CLK_DIV_2 (1 << 1)
+
+#define VV6410_FINE_EXPOSURE 320
+#define VV6410_COARSE_EXPOSURE 192
+#define VV6410_DEFAULT_GAIN 5
+
+#define VV6410_SUBSAMPLE 0x01
+#define VV6410_CROP_TO_QVGA 0x02
+
+static int vv6410_probe(struct sd *sd);
+static int vv6410_start(struct sd *sd);
+static int vv6410_init(struct sd *sd);
+static int vv6410_stop(struct sd *sd);
+static int vv6410_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+ .name = "ST VV6410",
+ .i2c_flush = 5,
+ .i2c_addr = 0x20,
+ .i2c_len = 1,
+ .init = vv6410_init,
+ .probe = vv6410_probe,
+ .start = vv6410_start,
+ .stop = vv6410_stop,
+ .dump = vv6410_dump,
+
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_hflip,
+ .get = vv6410_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_vflip,
+ .get = vv6410_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "analog gain",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_analog_gain,
+ .get = vv6410_get_analog_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ 356,
+ 292,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 356 * 292,
+ .bytesperline = 356,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+ }
+};
+
+/* If NULL, only single value to write, stored in len */
+struct stv_init {
+ const u8 *data;
+ u16 start;
+ u8 len;
+};
+
+static const u8 x1500[] = { /* 0x1500 - 0x150f */
+ 0x0b, 0xa7, 0xb7, 0x00, 0x00
+};
+
+static const u8 x1536[] = { /* 0x1536 - 0x153b */
+ 0x02, 0x00, 0x60, 0x01, 0x20, 0x01
+};
+
+static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */
+ 0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */
+};
+
+static const struct stv_init stv_bridge_init[] = {
+ /* This reg is written twice. Some kind of reset? */
+ {NULL, 0x1620, 0x80},
+ {NULL, 0x1620, 0x00},
+ {NULL, 0x1423, 0x04},
+ {x1500, 0x1500, ARRAY_SIZE(x1500)},
+ {x1536, 0x1536, ARRAY_SIZE(x1536)},
+ {x15c1, 0x15c1, ARRAY_SIZE(x15c1)}
+};
+
+static const u8 vv6410_sensor_init[][2] = {
+ /* Setup registers */
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ /* Use shuffled read-out mode */
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1 */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
+ /* Pre-clock generator divide off */
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+
+ /* Exposure registers */
+ {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8},
+ {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff},
+ {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8},
+ {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff},
+ {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN},
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+
+ /* System registers */
+ /* Enable voltage doubler */
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
+ /* Power up audio, differential */
+ {VV6410_AT1, BIT(4)|BIT(0)},
+};
+
+#endif
diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c
index 3a972ff1c..3692062d0 100644
--- a/linux/drivers/media/video/gspca/sunplus.c
+++ b/linux/drivers/media/video/gspca/sunplus.c
@@ -123,7 +123,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -136,7 +136,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format custom_mode[] = {
+static const struct v4l2_pix_format custom_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -149,7 +149,7 @@ static struct v4l2_pix_format custom_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format vga_mode2[] = {
+static const struct v4l2_pix_format vga_mode2[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c
index d2d6ad9d1..2d032aa0e 100644
--- a/linux/drivers/media/video/gspca/t613.c
+++ b/linux/drivers/media/video/gspca/t613.c
@@ -233,7 +233,7 @@ static char *effects_control[] = {
"Negative",
};
-static struct v4l2_pix_format vga_mode_t16[] = {
+static const struct v4l2_pix_format vga_mode_t16[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
@@ -499,7 +499,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
msleep(5);
i = 4;
- while (--i < 0) {
+ while (--i > 0) {
byte = reg_r(gspca_dev, 0x0060);
if (!(byte & 0x01))
break;
diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c
index 4e18f90ba..7d11a0d3e 100644
--- a/linux/drivers/media/video/gspca/tv8532.c
+++ b/linux/drivers/media/video/gspca/tv8532.c
@@ -73,7 +73,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c
index 4cc1f69f1..82dc818f4 100644
--- a/linux/drivers/media/video/gspca/vc032x.c
+++ b/linux/drivers/media/video/gspca/vc032x.c
@@ -126,7 +126,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vc0321_mode[] = {
+static const struct v4l2_pix_format vc0321_mode[] = {
{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 2,
@@ -138,7 +138,7 @@ static struct v4l2_pix_format vc0321_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format vc0323_mode[] = {
+static const struct v4l2_pix_format vc0323_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -151,7 +151,7 @@ static struct v4l2_pix_format vc0323_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format svga_mode[] = {
+static const struct v4l2_pix_format svga_mode[] = {
{800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 800,
.sizeimage = 800 * 600 * 1 / 4 + 590,
@@ -2452,6 +2452,7 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321},
diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c
index 678070b93..79d2a776d 100644
--- a/linux/drivers/media/video/gspca/zc3xx.c
+++ b/linux/drivers/media/video/gspca/zc3xx.c
@@ -173,7 +173,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -186,7 +186,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -7542,6 +7542,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0458, 0x700c)},
{USB_DEVICE(0x0458, 0x700f)},
{USB_DEVICE(0x0461, 0x0a00)},
+ {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
{USB_DEVICE(0x046d, 0x08a0)},
{USB_DEVICE(0x046d, 0x08a1)},
{USB_DEVICE(0x046d, 0x08a2)},
diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c
index 76279ed30..f77d76470 100644
--- a/linux/drivers/media/video/ivtv/ivtv-streams.c
+++ b/linux/drivers/media/video/ivtv/ivtv-streams.c
@@ -209,7 +209,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
itv->device.name, s->name);
s->v4l2dev->num = num;
- s->v4l2dev->parent = &itv->dev->dev;
+ s->v4l2dev->v4l2_dev = &itv->device;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
s->v4l2dev->tvnorms = V4L2_STD_ALL;
diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c
index def15e381..9d2c98b82 100644
--- a/linux/drivers/media/video/msp3400-driver.c
+++ b/linux/drivers/media/video/msp3400-driver.c
@@ -488,7 +488,7 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
}
#ifdef CONFIG_VIDEO_ALLOW_V4L1
-static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg)
+static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct msp_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c
index 9615b7678..bfd0ddb63 100644
--- a/linux/drivers/media/video/stk-webcam.c
+++ b/linux/drivers/media/video/stk-webcam.c
@@ -1263,6 +1263,25 @@ static int stk_vidioc_g_parm(struct file *filp,
return 0;
}
+static int stk_vidioc_enum_framesizes(struct file *filp,
+ void *priv, struct v4l2_frmsizeenum *frms)
+{
+ if (frms->index >= ARRAY_SIZE(stk_sizes))
+ return -EINVAL;
+ switch (frms->pixel_format) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_SBGGR8:
+ frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ frms->discrete.width = stk_sizes[frms->index].w;
+ frms->discrete.height = stk_sizes[frms->index].h;
+ return 0;
+ default: return -EINVAL;
+ }
+}
+
static struct file_operations v4l_stk_fops = {
.owner = THIS_MODULE,
.open = v4l_stk_open,
@@ -1297,6 +1316,7 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
.vidioc_g_ctrl = stk_vidioc_g_ctrl,
.vidioc_s_ctrl = stk_vidioc_s_ctrl,
.vidioc_g_parm = stk_vidioc_g_parm,
+ .vidioc_enum_framesizes = stk_vidioc_enum_framesizes,
};
static void stk_v4l_dev_release(struct video_device *vd)
diff --git a/linux/drivers/media/video/tda7432.c b/linux/drivers/media/video/tda7432.c
index 375b8d6d2..64a90a71b 100644
--- a/linux/drivers/media/video/tda7432.c
+++ b/linux/drivers/media/video/tda7432.c
@@ -47,9 +47,10 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/i2c-addr.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include "compat.h"
#ifndef VIDEO_AUDIO_BALANCE
@@ -80,6 +81,7 @@ I2C_CLIENT_INSMOD;
/* Structure of address and subaddresses for the tda7432 */
struct tda7432 {
+ struct v4l2_subdev sd;
int addr;
int input;
int volume;
@@ -87,10 +89,12 @@ struct tda7432 {
int bass, treble;
int lf, lr, rf, rr;
int loud;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+
+static inline struct tda7432 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda7432, sd);
+}
/* The TDA7432 is made by STS-Thompson
* http://www.st.com
@@ -225,45 +229,33 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda7432_write(struct i2c_client *client, int subaddr, int val)
+static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- v4l_dbg(2, debug,client,"In tda7432_write\n");
- v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_write\n");
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- v4l_err(client,"I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
return 0;
}
-/* I don't think we ever actually _read_ the chip... */
-#if 0
-static int tda7432_read(struct i2c_client *client)
-{
- unsigned char buffer;
- v4l_dbg(2, debug,client,"In tda7432_read\n");
- if (1 != i2c_master_recv(client,&buffer,1)) {
- v4l_err(client,"I/O error, trying (read)\n");
- return -1;
- }
- v4l_dbg(1, debug,client,"Read 0x%02x\n", buffer);
- return buffer;
-}
-#endif
-
-static int tda7432_set(struct i2c_client *client)
+static int tda7432_set(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tda7432 *t = to_state(sd);
unsigned char buf[16];
- v4l_dbg(2, debug,client,"In tda7432_set\n");
- v4l_dbg(1, debug,client,
+ v4l2_dbg(1, debug, sd,
"tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
- t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud);
+ t->input, t->volume, t->bass, t->treble, t->lf, t->lr,
+ t->rf, t->rr, t->loud);
buf[0] = TDA7432_IN;
buf[1] = t->input;
buf[2] = t->volume;
@@ -274,18 +266,19 @@ static int tda7432_set(struct i2c_client *client)
buf[7] = t->rf;
buf[8] = t->rr;
buf[9] = t->loud;
- if (10 != i2c_master_send(client,buf,10)) {
- v4l_err(client,"I/O error, trying tda7432_set\n");
+ if (10 != i2c_master_send(client, buf, 10)) {
+ v4l2_err(sd, "I/O error, trying tda7432_set\n");
return -1;
}
return 0;
}
-static void do_tda7432_init(struct i2c_client *client)
+static void do_tda7432_init(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
- v4l_dbg(2, debug,client,"In tda7432_init\n");
+ struct tda7432 *t = to_state(sd);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_init\n");
t->input = TDA7432_STEREO_IN | /* Main (stereo) input */
TDA7432_BASS_SYM | /* Symmetric bass cut */
@@ -302,57 +295,12 @@ static void do_tda7432_init(struct i2c_client *client)
t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */
t->loud = loudness; /* insmod parameter */
- tda7432_set(client);
-}
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct tda7432 *t;
- struct i2c_client *client;
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- do_tda7432_init(client);
- i2c_attach_client(client);
-
- v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name);
- return 0;
-}
-
-static int tda7432_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda7432_attach);
- return 0;
-}
-
-static int tda7432_detach(struct i2c_client *client)
-{
- struct tda7432 *t = i2c_get_clientdata(client);
-
- do_tda7432_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
+ tda7432_set(sd);
}
-static int tda7432_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -396,10 +344,9 @@ static int tda7432_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda7432_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -414,7 +361,7 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (loudness) /* Turn on the loudness bit */
t->volume |= TDA7432_LD_ON;
- tda7432_write(client,TDA7432_VL, t->volume);
+ tda7432_write(sd, TDA7432_VL, t->volume);
return 0;
case V4L2_CID_AUDIO_BALANCE:
if (ctrl->value < 32768) {
@@ -442,14 +389,14 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if(t->bass>= 0x8)
t->bass = (~t->bass & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
case V4L2_CID_AUDIO_TREBLE:
t->treble= ctrl->value >> 12;
if(t->treble>= 0x8)
t->treble = (~t->treble & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
default:
return -EINVAL;
@@ -459,92 +406,106 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (t->muted)
{
/* Mute & update balance*/
- tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE);
} else {
- tda7432_write(client,TDA7432_LF, t->lf);
- tda7432_write(client,TDA7432_LR, t->lr);
- tda7432_write(client,TDA7432_RF, t->rf);
- tda7432_write(client,TDA7432_RR, t->rr);
+ tda7432_write(sd, TDA7432_LF, t->lf);
+ tda7432_write(sd, TDA7432_LR, t->lr);
+ tda7432_write(sd, TDA7432_RF, t->rf);
+ tda7432_write(sd, TDA7432_RR, t->rr);
}
return 0;
}
-static int tda7432_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- v4l_dbg(2, debug,client,"In tda7432_command\n");
- if (debug>1)
- v4l_i2c_print_ioctl(client,cmd);
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda7432_set_ctrl(client, arg);
-
- case VIDIOC_G_CTRL:
- return tda7432_get_ctrl(client, arg);
-
- } /* end of (cmd) switch */
+ return -EINVAL;
+}
- return 0;
+static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda7432",
- },
- .id = I2C_DRIVERID_TDA7432,
- .attach_adapter = tda7432_probe,
- .detach_client = tda7432_detach,
- .command = tda7432_command,
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda7432_core_ops = {
+ .queryctrl = tda7432_queryctrl,
+ .g_ctrl = tda7432_g_ctrl,
+ .s_ctrl = tda7432_s_ctrl,
};
-static struct i2c_client client_template =
-{
- .name = "tda7432",
- .driver = &driver,
+static const struct v4l2_subdev_ops tda7432_ops = {
+ .core = &tda7432_core_ops,
};
-static int __init tda7432_init(void)
+/* ----------------------------------------------------------------------- */
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda7432_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- if ( (loudness < 0) || (loudness > 15) ) {
- printk(KERN_ERR "loudness parameter must be between 0 and 15\n");
- return -EINVAL;
+ struct tda7432 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda7432_ops);
+ if (loudness < 0 || loudness > 15) {
+ v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");
+ if (loudness < 0)
+ loudness = 0;
+ if (loudness > 15)
+ loudness = 15;
}
- return i2c_add_driver(&driver);
+ do_tda7432_init(sd);
+ return 0;
}
-static void __exit tda7432_fini(void)
+static int tda7432_remove(struct i2c_client *client)
{
- i2c_del_driver(&driver);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ do_tda7432_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
}
-module_init(tda7432_init);
-module_exit(tda7432_fini);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+static const struct i2c_device_id tda7432_id[] = {
+ { "tda7432", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda7432_id);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+#endif
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda7432",
+ .driverid = I2C_DRIVERID_TDA7432,
+ .command = tda7432_command,
+ .probe = tda7432_probe,
+ .remove = tda7432_remove,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ .id_table = tda7432_id,
+#endif
+};
diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c
index 31dde7516..7b749e4bb 100644
--- a/linux/drivers/media/video/tda9840.c
+++ b/linux/drivers/media/video/tda9840.c
@@ -29,7 +29,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tda9840.h"
#include "compat.h"
@@ -63,85 +63,89 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static void tda9840_write(struct i2c_client *client, u8 reg, u8 val)
+static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
if (i2c_smbus_write_byte_data(client, reg, val))
- v4l_dbg(1, debug, client, "error writing %02x to %02x\n",
+ v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n",
val, reg);
}
-static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
{
- int byte = *(int *)arg;
-
- switch (cmd) {
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *t = arg;
- int byte;
+ int byte;
- if (t->index)
- return -EINVAL;
+ if (t->index)
+ return -EINVAL;
- switch (t->audmode) {
- case V4L2_TUNER_MODE_STEREO:
- byte = TDA9840_SET_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- byte = TDA9840_SET_BOTH;
- break;
- case V4L2_TUNER_MODE_LANG1:
- byte = TDA9840_SET_LANG1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- byte = TDA9840_SET_LANG2;
- break;
- default:
- byte = TDA9840_SET_MONO;
- break;
- }
- v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte);
- tda9840_write(client, SWITCH, byte);
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ byte = TDA9840_SET_STEREO;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ byte = TDA9840_SET_LANG1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_MONO;
+ break;
+ }
+ v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
+ tda9840_write(sd, SWITCH, byte);
+ return 0;
+}
+
+static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (1 != i2c_master_recv(client, &byte, 1)) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_master_recv() failed\n");
+ return -EIO;
+ }
+
+ if (byte & 0x80) {
+ v4l2_dbg(1, debug, sd,
+ "TDA9840_DETECT: register contents invalid\n");
+ return -EINVAL;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *t = arg;
- u8 byte;
+ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+ switch (byte & 0x60) {
+ case 0x00:
t->rxsubchans = V4L2_TUNER_SUB_MONO;
- if (1 != i2c_master_recv(client, &byte, 1)) {
- v4l_dbg(1, debug, client,
- "i2c_master_recv() failed\n");
- return -EIO;
- }
-
- if (byte & 0x80) {
- v4l_dbg(1, debug, client,
- "TDA9840_DETECT: register contents invalid\n");
- return -EINVAL;
- }
-
- v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte);
-
- switch (byte & 0x60) {
- case 0x00:
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
- case 0x20:
- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- break;
- case 0x40:
- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
- break;
- default: /* Incorrect detect */
- t->rxsubchans = V4L2_TUNER_MODE_MONO;
- break;
- }
+ break;
+ case 0x20:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x40:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ break;
+ default: /* Incorrect detect */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
break;
}
+ return 0;
+}
+static int tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ int byte;
+
+ switch (cmd) {
case TDA9840_LEVEL_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -20)
@@ -153,11 +157,12 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
byte += 0x8;
else
byte = -byte;
- tda9840_write(client, LEVEL_ADJUST, byte);
+ tda9840_write(sd, LEVEL_ADJUST, byte);
break;
case TDA9840_STEREO_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -24)
@@ -170,18 +175,41 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
else
byte = -byte;
- tda9840_write(client, STEREO_ADJUST, byte);
+ tda9840_write(sd, STEREO_ADJUST, byte);
break;
default:
return -ENOIOCTLCMD;
}
-
return 0;
}
+static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda9840_core_ops = {
+ .ioctl = tda9840_ioctl,
+};
+
+static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
+ .s_tuner = tda9840_s_tuner,
+ .g_tuner = tda9840_g_tuner,
+};
+
+static const struct v4l2_subdev_ops tda9840_ops = {
+ .core = &tda9840_core_ops,
+ .tuner = &tda9840_tuner_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
static int tda9840_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int result;
int byte;
@@ -189,23 +217,38 @@ static int tda9840_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
+ return -EIO;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
+
/* set initial values for level & stereo - adjustment, mode */
byte = 0;
- result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte);
- result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte);
- tda9840_write(client, SWITCH, TDA9840_SET_STEREO);
+ result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte);
+ result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte);
+ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
if (result) {
- v4l_dbg(1, debug, client, "could not initialize tda9840\n");
+ v4l2_dbg(1, debug, sd, "could not initialize tda9840\n");
+ kfree(sd);
return -ENODEV;
}
return 0;
}
+static int tda9840_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
static int tda9840_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
@@ -225,6 +268,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TDA9840,
.command = tda9840_command,
.probe = tda9840_probe,
+ .remove = tda9840_remove,
.legacy_probe = tda9840_legacy_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = tda9840_id,
diff --git a/linux/drivers/media/video/tda9875.c b/linux/drivers/media/video/tda9875.c
index 20e738264..2e72c36a9 100644
--- a/linux/drivers/media/video/tda9875.c
+++ b/linux/drivers/media/video/tda9875.c
@@ -25,13 +25,12 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
#include <linux/i2c.h>
-#include <linux/init.h>
-#include "compat.h"
-
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/i2c-addr.h>
+#include "compat.h"
static int debug; /* insmod parameter */
module_param(debug, int, S_IRUGO | S_IWUSR);
@@ -47,13 +46,15 @@ I2C_CLIENT_INSMOD;
/* This is a superset of the TDA9875 */
struct tda9875 {
+ struct v4l2_subdev sd;
int rvol, lvol;
int bass, treble;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+static inline struct tda9875 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda9875, sd);
+}
#define dprintk if (debug) printk
@@ -106,15 +107,16 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char val)
+static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- dprintk("In tda9875_write\n");
- dprintk("Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
@@ -135,7 +137,7 @@ static int tda9875_read(struct i2c_client *client)
}
#endif
-static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
+static int i2c_read_register(struct i2c_client *client, int addr, int reg)
{
unsigned char write[1];
unsigned char read[1];
@@ -143,150 +145,83 @@ static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
{ addr, 0, 1, write },
{ addr, I2C_M_RD, 1, read }
};
+
write[0] = reg;
- if (2 != i2c_transfer(adap,msgs,2)) {
- printk(KERN_WARNING "tda9875: I/O error (read2)\n");
+ if (2 != i2c_transfer(client->adapter, msgs, 2)) {
+ v4l_warn(client, "I/O error (read2)\n");
return -1;
}
- dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]);
+ v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]);
return read[0];
}
-static void tda9875_set(struct i2c_client *client)
+static void tda9875_set(struct v4l2_subdev *sd)
{
- struct tda9875 *tda = i2c_get_clientdata(client);
+ struct tda9875 *tda = to_state(sd);
unsigned char a;
- dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",
- tda->lvol,tda->rvol,tda->bass,tda->treble);
-
+ v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n",
+ tda->lvol, tda->rvol, tda->bass, tda->treble);
a = tda->lvol & 0xff;
- tda9875_write(client, TDA9875_MVL, a);
+ tda9875_write(sd, TDA9875_MVL, a);
a =tda->rvol & 0xff;
- tda9875_write(client, TDA9875_MVR, a);
+ tda9875_write(sd, TDA9875_MVR, a);
a =tda->bass & 0xff;
- tda9875_write(client, TDA9875_MBA, a);
+ tda9875_write(sd, TDA9875_MBA, a);
a =tda->treble & 0xff;
- tda9875_write(client, TDA9875_MTR, a);
+ tda9875_write(sd, TDA9875_MTR, a);
}
-static void do_tda9875_init(struct i2c_client *client)
+static void do_tda9875_init(struct v4l2_subdev *sd)
{
- struct tda9875 *t = i2c_get_clientdata(client);
- dprintk("In tda9875_init\n");
- tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/
- tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/
- tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/
- tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/
- tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/
- tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/
- tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/
- tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/
- tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/
- tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/
- tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/
- tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/
- tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/
-
- tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Channel volume 1 mute*/
- tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Channel volume 2 mute */
- tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/
- tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/
- tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/
- tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */
- tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */
- tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */
- tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/
- tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/
- tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/
- tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/
- tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/
- tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/
- tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/
- tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/
-
- tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */
-
- t->lvol=t->rvol =0; /* 0dB */
- t->bass=0; /* 0dB */
- t->treble=0; /* 0dB */
- tda9875_set(client);
-
-}
-
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda9875_checkit(struct i2c_adapter *adap, int addr)
-{
- int dic,rev;
-
- dic=i2c_read_register(adap,addr,254);
- rev=i2c_read_register(adap,addr,255);
-
- if(dic==0 || dic==2) { // tda9875 and tda9875A
- printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n",
- dic==0?"":"A", rev,addr<<1);
- return 1;
- }
- printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev);
- return(0);
+ struct tda9875 *t = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "In tda9875_init\n");
+ tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
+ tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
+ tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/
+ tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
+ tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
+ tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
+ tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
+
+ tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
+ tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
+ tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
+ tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
+ tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
+ tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
+ tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
+ tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
+ tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
+ tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
+ tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
+ tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
+ tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
+ tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
+
+ tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */
+
+ t->lvol = t->rvol = 0; /* 0dB */
+ t->bass = 0; /* 0dB */
+ t->treble = 0; /* 0dB */
+ tda9875_set(sd);
}
-static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct tda9875 *t;
- struct i2c_client *client;
- dprintk("In tda9875_attach\n");
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- if(!tda9875_checkit(adap,addr)) {
- kfree(t);
- return 1;
- }
-
- do_tda9875_init(client);
- printk(KERN_INFO "tda9875: init\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tda9875_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda9875_attach);
- return 0;
-}
-
-static int tda9875_detach(struct i2c_client *client)
-{
- struct tda9875 *t = i2c_get_clientdata(client);
-
- do_tda9875_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
-}
-static int tda9875_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -318,10 +253,9 @@ static int tda9875_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda9875_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
int chvol=0, volume, balance, left, right;
switch (ctrl->id) {
@@ -385,85 +319,109 @@ static int tda9875_set_ctrl(struct i2c_client *client,
t->rvol = -84 & 0xff;
}
-//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble);
-
- tda9875_set(client);
-
+ tda9875_set(sd);
return 0;
}
-
-static int tda9875_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- dprintk("In tda9875_command...\n");
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda9875_set_ctrl(client, arg);
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return tda9875_get_ctrl(client, arg);
+static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
- default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */
+/* ----------------------------------------------------------------------- */
- /* nothing */
- dprintk("Default\n");
+static const struct v4l2_subdev_core_ops tda9875_core_ops = {
+ .queryctrl = tda9875_queryctrl,
+ .g_ctrl = tda9875_g_ctrl,
+ .s_ctrl = tda9875_s_ctrl,
+};
- } /* end of (cmd) switch */
+static const struct v4l2_subdev_ops tda9875_ops = {
+ .core = &tda9875_core_ops,
+};
- return 0;
-}
+/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda9875",
- },
- .id = I2C_DRIVERID_TDA9875,
- .attach_adapter = tda9875_probe,
- .detach_client = tda9875_detach,
- .command = tda9875_command,
-};
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
-static struct i2c_client client_template =
+static int tda9875_checkit(struct i2c_client *client, int addr)
{
- .name = "tda9875",
- .driver = &driver,
-};
+ int dic, rev;
-static int __init tda9875_init(void)
-{
- return i2c_add_driver(&driver);
+ dic = i2c_read_register(client, addr, 254);
+ rev = i2c_read_register(client, addr, 255);
+
+ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
+ v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n",
+ dic == 0 ? "" : "A", rev, addr << 1);
+ return 1;
+ }
+ v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n",
+ addr << 1, dic, rev);
+ return 0;
}
-static void __exit tda9875_fini(void)
+static int tda9875_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- i2c_del_driver(&driver);
+ struct tda9875 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ if (!tda9875_checkit(client, client->addr))
+ return -ENODEV;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda9875_ops);
+
+ do_tda9875_init(sd);
+ return 0;
}
-module_init(tda9875_init);
-module_exit(tda9875_fini);
+static int tda9875_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+ do_tda9875_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+static const struct i2c_device_id tda9875_id[] = {
+ { "tda9875", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda9875_id);
+
+#endif
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda9875",
+ .driverid = I2C_DRIVERID_TDA9875,
+ .command = tda9875_command,
+ .probe = tda9875_probe,
+ .remove = tda9875_remove,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ .id_table = tda9875_id,
+#endif
+};
diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c
index 073f020fa..21900def4 100644
--- a/linux/drivers/media/video/tea6415c.c
+++ b/linux/drivers/media/video/tea6415c.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6415c.h"
#include "compat.h"
@@ -123,31 +123,57 @@ static int switch_matrix(struct i2c_client *client, int i, int o)
return ret;
}
-static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6415C_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- switch (cmd) {
- case TEA6415C_SWITCH:
- result = switch_matrix(client, v->in, v->out);
- break;
- default:
- return -ENOIOCTLCMD;
+ return switch_matrix(client, v->in, v->out);
}
- return result;
+ return -ENOIOCTLCMD;
}
+static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+ .ioctl = tea6415c_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6415c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* let's see whether this adapter can support what we need */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
return 0;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
+ return 0;
+}
+
+static int tea6415c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -171,6 +197,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6415C,
.command = tea6415c_command,
.probe = tea6415c_probe,
+ .remove = tea6415c_remove,
.legacy_probe = tea6415c_legacy_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = tea6415c_id,
diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c
index 8ded72ade..aee419402 100644
--- a/linux/drivers/media/video/tea6420.c
+++ b/linux/drivers/media/video/tea6420.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6420.h"
#include "compat.h"
@@ -91,26 +91,37 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
return 0;
}
-static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6420_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- switch (cmd) {
- case TEA6420_SWITCH:
- result = tea6420_switch(client, a->in, a->out, a->gain);
- break;
- default:
- return -ENOIOCTLCMD;
+ return tea6420_switch(client, a->in, a->out, a->gain);
}
+ return -ENOIOCTLCMD;
+}
- return result;
+static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6420_core_ops = {
+ .ioctl = tea6420_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6420_ops = {
+ .core = &tea6420_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6420_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int err, i;
/* let's see whether this adapter can support what we need */
@@ -127,9 +138,22 @@ static int tea6420_probe(struct i2c_client *client,
}
if (err) {
v4l_dbg(1, debug, client, "could not initialize tea6420\n");
- kfree(client);
return -ENODEV;
}
+
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
+ return 0;
+}
+
+static int tea6420_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -153,6 +177,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6420,
.command = tea6420_command,
.probe = tea6420_probe,
+ .remove = tea6420_remove,
.legacy_probe = tea6420_legacy_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = tea6420_id,
diff --git a/linux/drivers/media/video/tlv320aic23b.c b/linux/drivers/media/video/tlv320aic23b.c
index a531a46d1..ecc3a4ef6 100644
--- a/linux/drivers/media/video/tlv320aic23b.c
+++ b/linux/drivers/media/video/tlv320aic23b.c
@@ -30,7 +30,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "compat.h"
@@ -45,15 +45,22 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
struct tlv320aic23b_state {
+ struct v4l2_subdev sd;
u8 muted;
};
-static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
+static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tlv320aic23b_state, sd);
+}
+
+static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if ((reg < 0 || reg > 9) && (reg != 15)) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -61,61 +68,82 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int tlv320aic23b_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct tlv320aic23b_state *state = i2c_get_clientdata(client);
- struct v4l2_control *ctrl = arg;
- u32 *freq = arg;
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- switch (*freq) {
- case 32000: /* set sample rate to 32 kHz */
- tlv320aic23b_write(client, 8, 0x018);
- break;
- case 44100: /* set sample rate to 44.1 kHz */
- tlv320aic23b_write(client, 8, 0x022);
- break;
- case 48000: /* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
- break;
- default:
- return -EINVAL;
- }
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
+ switch (freq) {
+ case 32000: /* set sample rate to 32 kHz */
+ tlv320aic23b_write(sd, 8, 0x018);
break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- tlv320aic23b_write(client, 0, 0x180); /* mute both channels */
- /* set gain on both channels to +3.0 dB */
- if (!state->muted)
- tlv320aic23b_write(client, 0, 0x119);
+ case 44100: /* set sample rate to 44.1 kHz */
+ tlv320aic23b_write(sd, 8, 0x022);
break;
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %s\n",
- state->muted ? "muted" : "active");
+ case 48000: /* set sample rate to 48 kHz */
+ tlv320aic23b_write(sd, 8, 0x000);
break;
-
default:
return -EINVAL;
}
return 0;
}
+static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
+ /* set gain on both channels to +3.0 dB */
+ if (!state->muted)
+ tlv320aic23b_write(sd, 0, 0x119);
+ return 0;
+}
+
+static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active");
+ return 0;
+}
+
+static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
+ .log_status = tlv320aic23b_log_status,
+ .g_ctrl = tlv320aic23b_g_ctrl,
+ .s_ctrl = tlv320aic23b_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = {
+ .s_clock_freq = tlv320aic23b_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops tlv320aic23b_ops = {
+ .core = &tlv320aic23b_core_ops,
+ .audio = &tlv320aic23b_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -129,6 +157,7 @@ static int tlv320aic23b_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tlv320aic23b_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -137,32 +166,36 @@ static int tlv320aic23b_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops);
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize tlv320aic23b */
/* RESET */
- tlv320aic23b_write(client, 15, 0x000);
+ tlv320aic23b_write(sd, 15, 0x000);
/* turn off DAC & mic input */
- tlv320aic23b_write(client, 6, 0x00A);
+ tlv320aic23b_write(sd, 6, 0x00A);
/* left-justified, 24-bit, master mode */
- tlv320aic23b_write(client, 7, 0x049);
+ tlv320aic23b_write(sd, 7, 0x049);
/* set gain on both channels to +3.0 dB */
- tlv320aic23b_write(client, 0, 0x119);
+ tlv320aic23b_write(sd, 0, 0x119);
/* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
+ tlv320aic23b_write(sd, 8, 0x000);
/* activate digital interface */
- tlv320aic23b_write(client, 9, 0x001);
+ tlv320aic23b_write(sd, 9, 0x001);
return 0;
}
static int tlv320aic23b_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c
index 8f1ca6567..343ab2d8b 100644
--- a/linux/drivers/media/video/tuner-core.c
+++ b/linux/drivers/media/video/tuner-core.c
@@ -810,7 +810,7 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
}
#ifdef CONFIG_VIDEO_ALLOW_V4L1
-static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg)
+static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct tuner *t = to_tuner(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c
index 0a86c80a2..492da4d8e 100644
--- a/linux/drivers/media/video/tvaudio.c
+++ b/linux/drivers/media/video/tvaudio.c
@@ -36,8 +36,7 @@
#include "compat.h"
#include <media/tvaudio.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -113,7 +112,7 @@ struct CHIPDESC {
/* current state of the chip */
struct CHIPSTATE {
- struct i2c_client *c;
+ struct v4l2_subdev sd;
/* chip-specific description - should point to
an entry at CHIPDESC table */
@@ -135,6 +134,11 @@ struct CHIPSTATE {
int audmode;
};
+static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct CHIPSTATE, sd);
+}
+
/* ---------------------------------------------------------------------- */
/* i2c addresses */
@@ -155,34 +159,34 @@ I2C_CLIENT_INSMOD;
static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
if (subaddr < 0) {
- v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n",
- chip->c->name, val);
+ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
chip->shadow.bytes[1] = val;
buffer[0] = val;
- if (1 != i2c_master_send(chip->c,buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n",
- chip->c->name, val);
+ if (1 != i2c_master_send(c, buffer, 1)) {
+ v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
return -1;
}
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n",
- chip->c->name, subaddr, val);
+ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
+ subaddr, val);
chip->shadow.bytes[subaddr+1] = val;
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(chip->c,buffer,2)) {
- v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n",
- chip->c->name, subaddr, val);
+ if (2 != i2c_master_send(c, buffer, 2)) {
+ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
+ subaddr, val);
return -1;
}
}
@@ -192,12 +196,14 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
static int chip_write_masked(struct CHIPSTATE *chip,
int subaddr, int val, int mask)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
if (mask != 0) {
if (subaddr < 0) {
val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
@@ -211,45 +217,51 @@ static int chip_write_masked(struct CHIPSTATE *chip,
static int chip_read(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer;
- if (1 != i2c_master_recv(chip->c,&buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (read)\n",
- chip->c->name);
+ if (1 != i2c_master_recv(c, &buffer, 1)) {
+ v4l2_warn(sd, "I/O error (read)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer);
+ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
return buffer;
}
static int chip_read2(struct CHIPSTATE *chip, int subaddr)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char write[1];
unsigned char read[1];
struct i2c_msg msgs[2] = {
- { chip->c->addr, 0, 1, write },
- { chip->c->addr, I2C_M_RD, 1, read }
+ { c->addr, 0, 1, write },
+ { c->addr, I2C_M_RD, 1, read }
};
+
write[0] = subaddr;
- if (2 != i2c_transfer(chip->c->adapter,msgs,2)) {
- v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name);
+ if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ v4l2_warn(sd, "I/O error (read2)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n",
- chip->c->name, subaddr,read[0]);
+ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
+ subaddr, read[0]);
return read[0];
}
static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
int i;
if (0 == cmd->count)
return 0;
if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register range: %d to %d\n",
cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
return -EINVAL;
@@ -258,19 +270,19 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
/* FIXME: it seems that the shadow bytes are wrong bellow !*/
/* update our shadow register set; print bytes if (debug > 0) */
- v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:",
- chip->c->name, name,cmd->bytes[0]);
+ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
+ name, cmd->bytes[0]);
for (i = 1; i < cmd->count; i++) {
if (debug)
- printk(" 0x%x",cmd->bytes[i]);
+ printk(KERN_CONT " 0x%x", cmd->bytes[i]);
chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
}
if (debug)
- printk("\n");
+ printk(KERN_CONT "\n");
/* send data to the chip */
- if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) {
- v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name);
+ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ v4l2_warn(sd, "I/O error (%s)\n", name);
return -1;
}
return 0;
@@ -293,9 +305,10 @@ static int chip_thread(void *data)
{
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chip->desc;
+ struct v4l2_subdev *sd = &chip->sd;
int mode;
- v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread started\n");
set_freezable();
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -305,7 +318,7 @@ static int chip_thread(void *data)
try_to_freeze();
if (kthread_should_stop())
break;
- v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread wakeup\n");
/* don't do anything for radio or if mode != auto */
if (chip->radio || chip->mode != 0)
@@ -317,8 +330,7 @@ static int chip_thread(void *data)
continue;
/* chip detected a new audio mode - set it */
- v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n",
- chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread checkmode\n");
chip->prevmode = mode;
@@ -337,7 +349,7 @@ static int chip_thread(void *data)
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread exiting\n");
return 0;
}
@@ -366,6 +378,7 @@ static int chip_thread(void *data)
static int tda9840_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val, mode;
val = chip_read(chip);
@@ -375,7 +388,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip)
if (val & TDA9840_ST_STEREO)
mode |= V4L2_TUNER_MODE_STEREO;
- v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
@@ -671,6 +684,7 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
static int tda9873_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val,mode;
val = chip_read(chip);
@@ -679,23 +693,24 @@ static int tda9873_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_STEREO;
if (val & TDA9873_DUAL)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
/* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n");
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
return;
}
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data);
switch (mode) {
case V4L2_TUNER_MODE_MONO:
@@ -716,7 +731,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
}
chip_write(chip, TDA9873_SW, sw_data);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
mode, sw_data);
}
@@ -825,6 +840,8 @@ static struct tda9874a_MODES {
static int tda9874a_setup(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
@@ -855,13 +872,14 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
}
- v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
return 1;
}
static int tda9874a_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dsr,nsr,mode;
int necr; /* just for debugging */
@@ -903,16 +921,18 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
}
- v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
dsr, nsr, necr, mode);
return mode;
}
static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
/* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
/* If auto-muting is disabled, we can hear a signal of degrading quality. */
- if(tda9874a_mode) {
+ if (tda9874a_mode) {
if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
tda9874a_NCONR &= 0xfe; /* enable */
else
@@ -949,7 +969,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_AOSR, aosr);
chip_write(chip, TDA9874A_MDACOSR, mdacosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
mode, aosr, mdacosr);
} else { /* dic == 0x07 */
@@ -984,13 +1004,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_FMMR, fmmr);
chip_write(chip, TDA9874A_AOSR, aosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
mode, fmmr, aosr);
}
}
static int tda9874a_checkit(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dic,sic; /* device id. and software id. codes */
if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
@@ -998,10 +1019,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip)
if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
return 0;
- v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
if((dic == 0x11)||(dic == 0x07)) {
- v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h");
+ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
tda9874a_dic = dic; /* remember device id. */
return 1;
}
@@ -1121,12 +1142,12 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
static int tda8425_initialize(struct CHIPSTATE *chip)
{
struct CHIPDESC *desc = chip->desc;
+ struct i2c_client *c = v4l2_get_subdevdata(&chip->sd);
int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1,
/* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF};
- if (chip->c->adapter->id == I2C_HW_B_RIVA) {
- memcpy (desc->inputmap, inputmap, sizeof (inputmap));
- }
+ if (c->adapter->id == I2C_HW_B_RIVA)
+ memcpy(desc->inputmap, inputmap, sizeof(inputmap));
return 0;
}
@@ -1223,9 +1244,11 @@ static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT
static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int update = 1;
audiocmd *t = NULL;
- v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+
+ v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
switch(mode){
case V4L2_TUNER_MODE_MONO:
@@ -1487,146 +1510,11 @@ static struct CHIPDESC chiplist[] = {
/* ---------------------------------------------------------------------- */
-/* i2c registration */
-static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct CHIPSTATE *chip;
- struct CHIPDESC *desc;
-
- if (debug) {
- printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
- printk(KERN_INFO "tvaudio: known chips: ");
- for (desc = chiplist; desc->name != NULL; desc++)
- printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
- printk("\n");
- }
-
- chip = kzalloc(sizeof(*chip),GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- chip->c = client;
- i2c_set_clientdata(client, chip);
-
- /* find description for the chip */
- v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1);
- for (desc = chiplist; desc->name != NULL; desc++) {
- if (0 == *(desc->insmodopt))
- continue;
- if (client->addr < desc->addr_lo ||
- client->addr > desc->addr_hi)
- continue;
- if (desc->checkit && !desc->checkit(chip))
- continue;
- break;
- }
- if (desc->name == NULL) {
- v4l_dbg(1, debug, client, "no matching chip description found\n");
- kfree(chip);
- return -EIO;
- }
- v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
- if (desc->flags) {
- v4l_dbg(1, debug, client, "matches:%s%s%s.\n",
- (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
- (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
- (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
- }
-
- /* fill required data structures */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
- strlcpy(client->name, desc->name, I2C_NAME_SIZE);
-#else
- if (!id)
- strlcpy(client->name, desc->name, I2C_NAME_SIZE);
-#endif
- chip->desc = desc;
- chip->shadow.count = desc->registers+1;
- chip->prevmode = -1;
- chip->audmode = V4L2_TUNER_MODE_LANG1;
-
- /* initialization */
- if (desc->initialize != NULL)
- desc->initialize(chip);
- else
- chip_cmd(chip,"init",&desc->init);
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- if (!desc->volfunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without volume controls
- */
- v4l_info(chip->c, "volume callback undefined!\n");
- desc->flags &= ~CHIP_HAS_VOLUME;
- } else {
- chip->left = desc->leftinit ? desc->leftinit : 65535;
- chip->right = desc->rightinit ? desc->rightinit : 65535;
- chip_write(chip, desc->leftreg,
- desc->volfunc(chip->left));
- chip_write(chip, desc->rightreg,
- desc->volfunc(chip->right));
- }
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- if (!desc->bassfunc || !desc->treblefunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without bass/treble controls
- */
- v4l_info(chip->c, "bass/treble callbacks undefined!\n");
- desc->flags &= ~CHIP_HAS_BASSTREBLE;
- } else {
- chip->treble = desc->trebleinit ?
- desc->trebleinit : 32768;
- chip->bass = desc->bassinit ?
- desc->bassinit : 32768;
- chip_write(chip, desc->bassreg,
- desc->bassfunc(chip->bass));
- chip_write(chip, desc->treblereg,
- desc->treblefunc(chip->treble));
- }
- }
-
- chip->thread = NULL;
- if (desc->flags & CHIP_NEED_CHECKMODE) {
- if (!desc->getmode || !desc->setmode) {
- /* This shouldn't be happen. Warn user, but keep working
- without kthread
- */
- v4l_info(chip->c, "set/get mode callbacks undefined!\n");
- return 0;
- }
- /* start async thread */
- init_timer(&chip->wt);
- chip->wt.function = chip_thread_wake;
- chip->wt.data = (unsigned long)chip;
- chip->thread = kthread_run(chip_thread, chip, chip->c->name);
- if (IS_ERR(chip->thread)) {
- v4l_warn(chip->c, "%s: failed to create kthread\n",
- chip->c->name);
- chip->thread = NULL;
- }
- }
- return 0;
-}
-
-static int chip_remove(struct i2c_client *client)
-{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
-
- del_timer_sync(&chip->wt);
- if (chip->thread) {
- /* shutdown async thread */
- kthread_stop(chip->thread);
- chip->thread = NULL;
- }
-
- kfree(chip);
- return 0;
-}
-
-static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1664,9 +1552,10 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
return -EINVAL;
}
-static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1738,161 +1627,331 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
/* ---------------------------------------------------------------------- */
/* video4linux interface */
-static int chip_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tvaudio_s_radio(struct v4l2_subdev *sd)
{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
- struct CHIPDESC *desc = chip->desc;
+ struct CHIPSTATE *chip = to_state(sd);
- if (debug > 0) {
- v4l_i2c_print_ioctl(chip->c, cmd);
- printk("\n");
+ chip->radio = 1;
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ return 0;
+}
+
+static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ return -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return v4l2_ctrl_query_fill_std(qc);
+}
+
+static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = rt->input;
+ if (chip->muted)
+ return 0;
+ chip_write_masked(chip, desc->inputreg,
+ desc->inputmap[chip->input], desc->inputmask);
+ return 0;
+}
+
+static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = 0;
+
+ if (chip->radio)
+ return 0;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ mode = vt->audmode;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ mode = V4L2_TUNER_MODE_STEREO;
+ break;
+ default:
+ return -EINVAL;
}
+ chip->audmode = vt->audmode;
- switch (cmd) {
- case AUDC_SET_RADIO:
- chip->radio = 1;
+ if (desc->setmode && mode) {
chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
- break;
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- if (!(desc->flags & CHIP_HAS_VOLUME))
- return -EINVAL;
- break;
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- return v4l2_ctrl_query_fill_std(qc);
+ chip->mode = mode;
+ desc->setmode(chip, mode);
}
- case VIDIOC_S_CTRL:
- return tvaudio_set_ctrl(chip, arg);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return tvaudio_get_ctrl(chip, arg);
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = V4L2_TUNER_MODE_MONO;
- rt->input = chip->input;
- rt->output = 0;
- break;
+ if (chip->radio)
+ return 0;
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = 0;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+ if (desc->getmode)
+ mode = desc->getmode(chip);
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ /* Note: for SAP it should be mono/lang2 or stereo/lang2.
+ When this module is converted fully to v4l2, then this
+ should change for those chips that can detect SAP. */
+ if (mode & V4L2_TUNER_MODE_LANG1)
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ return 0;
+}
+
+static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 0;
+ return 0;
+}
+
+static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ chip->mode = 0; /* automatic */
+
+ /* For chips that provide getmode and setmode, and doesn't
+ automatically follows the stereo carrier, a kthread is
+ created to set the audio standard. In this case, when then
+ the video channel is changed, tvaudio starts on MONO mode.
+ After waiting for 2 seconds, the kernel thread is called,
+ to follow whatever audio standard is pointed by the
+ audio carrier.
+ */
+ if (chip->thread) {
+ desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+ if (chip->prevmode != V4L2_TUNER_MODE_MONO)
+ chip->prevmode = -1; /* reset previous mode */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+ return 0;
+}
- if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
- return -EINVAL;
- /* There are four inputs: tuner, radio, extern and intern. */
- chip->input = rt->input;
- if (chip->muted)
- break;
- chip_write_masked(chip, desc->inputreg,
- desc->inputmap[chip->input], desc->inputmask);
- break;
+static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0);
+}
+
+static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
+ .g_chip_ident = tvaudio_g_chip_ident,
+ .queryctrl = tvaudio_queryctrl,
+ .g_ctrl = tvaudio_g_ctrl,
+ .s_ctrl = tvaudio_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
+ .s_radio = tvaudio_s_radio,
+ .s_frequency = tvaudio_s_frequency,
+ .s_std = tvaudio_s_std,
+ .s_tuner = tvaudio_s_tuner,
+ .s_tuner = tvaudio_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
+ .s_routing = tvaudio_s_routing,
+};
+
+static const struct v4l2_subdev_ops tvaudio_ops = {
+ .core = &tvaudio_core_ops,
+ .tuner = &tvaudio_tuner_ops,
+ .audio = &tvaudio_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c registration */
+
+static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+ struct v4l2_subdev *sd;
+
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = 0;
- if (chip->radio)
- break;
- switch (vt->audmode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- case V4L2_TUNER_MODE_LANG2:
- mode = vt->audmode;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- mode = V4L2_TUNER_MODE_STEREO;
- break;
- default:
- return -EINVAL;
- }
- chip->audmode = vt->audmode;
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ sd = &chip->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
- if (desc->setmode && mode) {
- chip->watch_stereo = 0;
- /* del_timer(&chip->wt); */
- chip->mode = mode;
- desc->setmode(chip, mode);
- }
+ /* find description for the chip */
+ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
break;
}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = V4L2_TUNER_MODE_MONO;
+ if (desc->name == NULL) {
+ v4l2_dbg(1, debug, sd, "no matching chip description found\n");
+ kfree(chip);
+ return -EIO;
+ }
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
+ if (desc->flags) {
+ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+ }
- if (chip->radio)
- break;
- vt->audmode = chip->audmode;
- vt->rxsubchans = 0;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ /* fill required data structures */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+#else
+ if (!id)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+#endif
+ chip->desc = desc;
+ chip->shadow.count = desc->registers+1;
+ chip->prevmode = -1;
+ chip->audmode = V4L2_TUNER_MODE_LANG1;
- if (desc->getmode)
- mode = desc->getmode(chip);
+ /* initialization */
+ if (desc->initialize != NULL)
+ desc->initialize(chip);
+ else
+ chip_cmd(chip, "init", &desc->init);
- if (mode & V4L2_TUNER_MODE_MONO)
- vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
- if (mode & V4L2_TUNER_MODE_STEREO)
- vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
- /* Note: for SAP it should be mono/lang2 or stereo/lang2.
- When this module is converted fully to v4l2, then this
- should change for those chips that can detect SAP. */
- if (mode & V4L2_TUNER_MODE_LANG1)
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
- V4L2_TUNER_SUB_LANG2;
- break;
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ if (!desc->volfunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without volume controls
+ */
+ v4l2_info(sd, "volume callback undefined!\n");
+ desc->flags &= ~CHIP_HAS_VOLUME;
+ } else {
+ chip->left = desc->leftinit ? desc->leftinit : 65535;
+ chip->right = desc->rightinit ? desc->rightinit : 65535;
+ chip_write(chip, desc->leftreg,
+ desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg,
+ desc->volfunc(chip->right));
+ }
}
- case VIDIOC_S_STD:
- chip->radio = 0;
- break;
- case VIDIOC_S_FREQUENCY:
- chip->mode = 0; /* automatic */
-
- /* For chips that provide getmode and setmode, and doesn't
- automatically follows the stereo carrier, a kthread is
- created to set the audio standard. In this case, when then
- the video channel is changed, tvaudio starts on MONO mode.
- After waiting for 2 seconds, the kernel thread is called,
- to follow whatever audio standard is pointed by the
- audio carrier.
- */
- if (chip->thread) {
- desc->setmode(chip,V4L2_TUNER_MODE_MONO);
- if (chip->prevmode != V4L2_TUNER_MODE_MONO)
- chip->prevmode = -1; /* reset previous mode */
- mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ if (!desc->bassfunc || !desc->treblefunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without bass/treble controls
+ */
+ v4l2_info(sd, "bass/treble callbacks undefined!\n");
+ desc->flags &= ~CHIP_HAS_BASSTREBLE;
+ } else {
+ chip->treble = desc->trebleinit ?
+ desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ?
+ desc->bassinit : 32768;
+ chip_write(chip, desc->bassreg,
+ desc->bassfunc(chip->bass));
+ chip_write(chip, desc->treblereg,
+ desc->treblefunc(chip->treble));
}
- break;
+ }
+
+ chip->thread = NULL;
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getmode || !desc->setmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without kthread
+ */
+ v4l2_info(sd, "set/get mode callbacks undefined!\n");
+ return 0;
+ }
+ /* start async thread */
+ init_timer(&chip->wt);
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+ if (IS_ERR(chip->thread)) {
+ v4l2_warn(sd, "failed to create kthread\n");
+ chip->thread = NULL;
+ }
+ }
+ return 0;
+}
+
+static int tvaudio_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct CHIPSTATE *chip = to_state(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0);
+ del_timer_sync(&chip->wt);
+ if (chip->thread) {
+ /* shutdown async thread */
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
}
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(chip);
return 0;
}
-static int chip_legacy_probe(struct i2c_adapter *adap)
+static int tvaudio_legacy_probe(struct i2c_adapter *adap)
{
/* don't attach on saa7146 based cards,
because dedicated drivers are used */
@@ -1907,21 +1966,21 @@ static int chip_legacy_probe(struct i2c_adapter *adap)
/* This driver supports many devices and the idea is to let the driver
detect which device is present. So rather than listing all supported
devices here, we pretend to support a single, fake device type. */
-static const struct i2c_device_id chip_id[] = {
+static const struct i2c_device_id tvaudio_id[] = {
{ "tvaudio", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, chip_id);
+MODULE_DEVICE_TABLE(i2c, tvaudio_id);
#endif
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tvaudio",
.driverid = I2C_DRIVERID_TVAUDIO,
- .command = chip_command,
- .probe = chip_probe,
- .remove = chip_remove,
- .legacy_probe = chip_legacy_probe,
+ .command = tvaudio_command,
+ .probe = tvaudio_probe,
+ .remove = tvaudio_remove,
+ .legacy_probe = tvaudio_legacy_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
- .id_table = chip_id,
+ .id_table = tvaudio_id,
#endif
};
diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c
index ac8205d87..404df4aa2 100644
--- a/linux/drivers/media/video/tvp5150.c
+++ b/linux/drivers/media/video/tvp5150.c
@@ -10,8 +10,10 @@
#include <linux/videodev2.h>
#include <linux/delay.h>
#include <linux/video_decoder.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/tvp5150.h>
+#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-chip-ident.h>
#include "tvp5150_reg.h"
@@ -30,21 +32,7 @@ I2C_CLIENT_INSMOD;
static int debug;
module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-#define tvp5150_err(fmt, arg...) do { \
- printk(KERN_ERR "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_info(fmt, arg...) do { \
- printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_dbg(num, fmt, arg...) \
- do { \
- if (debug >= num) \
- printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\
- c->driver->driver.name, \
- i2c_adapter_id(c->adapter), \
- c->addr , ## arg); } while (0)
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
/* supported controls */
static struct v4l2_queryctrl tvp5150_qctrl[] = {
@@ -88,7 +76,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
};
struct tvp5150 {
- struct i2c_client *client;
+ struct v4l2_subdev sd;
v4l2_std_id norm; /* Current set standard */
struct v4l2_routing route;
@@ -99,49 +87,57 @@ struct tvp5150 {
int sat;
};
-static int tvp5150_read(struct i2c_client *c, unsigned char addr)
+static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tvp5150, sd);
+}
+
+static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[1];
int rc;
buffer[0] = addr;
if (1 != (rc = i2c_master_send(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
msleep(10);
if (1 != (rc = i2c_master_recv(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
return (buffer[0]);
}
-static inline void tvp5150_write(struct i2c_client *c, unsigned char addr,
+static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
unsigned char value)
{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
int rc;
buffer[0] = addr;
buffer[1] = value;
- tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
+ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
if (2 != (rc = i2c_master_send(c, buffer, 2)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
}
-static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,int max_line)
+static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
+ const u8 end, int max_line)
{
- int i=0;
+ int i = 0;
- while (init!=(u8)(end+1)) {
- if ((i%max_line) == 0) {
- if (i>0)
+ while (init != (u8)(end + 1)) {
+ if ((i % max_line) == 0) {
+ if (i > 0)
printk("\n");
- printk("tvp5150: %s reg 0x%02x = ",s,init);
+ printk("tvp5150: %s reg 0x%02x = ", s, init);
}
- printk("%02x ",tvp5150_read(c, init));
+ printk("%02x ", tvp5150_read(sd, init));
init++;
i++;
@@ -149,151 +145,152 @@ static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,
printk("\n");
}
-static void dump_reg(struct i2c_client *c)
+static int tvp5150_log_status(struct v4l2_subdev *sd)
{
printk("tvp5150: Video input source selection #1 = 0x%02x\n",
- tvp5150_read(c, TVP5150_VD_IN_SRC_SEL_1));
+ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
printk("tvp5150: Analog channel controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_ANAL_CHL_CTL));
+ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
printk("tvp5150: Operation mode controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_OP_MODE_CTL));
+ tvp5150_read(sd, TVP5150_OP_MODE_CTL));
printk("tvp5150: Miscellaneous controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_MISC_CTL));
+ tvp5150_read(sd, TVP5150_MISC_CTL));
printk("tvp5150: Autoswitch mask= 0x%02x\n",
- tvp5150_read(c, TVP5150_AUTOSW_MSK));
+ tvp5150_read(sd, TVP5150_AUTOSW_MSK));
printk("tvp5150: Color killer threshold control = 0x%02x\n",
- tvp5150_read(c, TVP5150_COLOR_KIL_THSH_CTL));
+ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_2),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_3));
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
printk("tvp5150: Brightness control = 0x%02x\n",
- tvp5150_read(c, TVP5150_BRIGHT_CTL));
+ tvp5150_read(sd, TVP5150_BRIGHT_CTL));
printk("tvp5150: Color saturation control = 0x%02x\n",
- tvp5150_read(c, TVP5150_SATURATION_CTL));
+ tvp5150_read(sd, TVP5150_SATURATION_CTL));
printk("tvp5150: Hue control = 0x%02x\n",
- tvp5150_read(c, TVP5150_HUE_CTL));
+ tvp5150_read(sd, TVP5150_HUE_CTL));
printk("tvp5150: Contrast control = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONTRAST_CTL));
+ tvp5150_read(sd, TVP5150_CONTRAST_CTL));
printk("tvp5150: Outputs and data rates select = 0x%02x\n",
- tvp5150_read(c, TVP5150_DATA_RATE_SEL));
+ tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
printk("tvp5150: Configuration shared pins = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONF_SHARED_PIN));
+ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
printk("tvp5150: Genlock/RTC = 0x%02x\n",
- tvp5150_read(c, TVP5150_GENLOCK));
+ tvp5150_read(sd, TVP5150_GENLOCK));
printk("tvp5150: Horizontal sync start = 0x%02x\n",
- tvp5150_read(c, TVP5150_HORIZ_SYNC_START));
+ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
printk("tvp5150: Vertical blanking start = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_START));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
printk("tvp5150: Vertical blanking stop = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_STOP));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_2));
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
printk("tvp5150: Interrupt reset register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_RESET_REG_B));
+ tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
printk("tvp5150: Interrupt enable register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INTT_CONFIG_REG_B));
+ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
printk("tvp5150: Video standard = 0x%02x\n",
- tvp5150_read(c, TVP5150_VIDEO_STD));
+ tvp5150_read(sd, TVP5150_VIDEO_STD));
printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
- tvp5150_read(c, TVP5150_CB_GAIN_FACT),
- tvp5150_read(c, TVP5150_CR_GAIN_FACTOR));
+ tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
+ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
printk("tvp5150: Macrovision on counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_ON_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
printk("tvp5150: Macrovision off counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_OFF_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
- (tvp5150_read(c, TVP5150_REV_SELECT)&1)?3:4);
+ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
printk("tvp5150: Device ID = %02x%02x\n",
- tvp5150_read(c, TVP5150_MSB_DEV_ID),
- tvp5150_read(c, TVP5150_LSB_DEV_ID));
+ tvp5150_read(sd, TVP5150_MSB_DEV_ID),
+ tvp5150_read(sd, TVP5150_LSB_DEV_ID));
printk("tvp5150: ROM version = (hex) %02x.%02x\n",
- tvp5150_read(c, TVP5150_ROM_MAJOR_VER),
- tvp5150_read(c, TVP5150_ROM_MINOR_VER));
+ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
printk("tvp5150: Vertical line count = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_MSB),
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_LSB));
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
printk("tvp5150: Interrupt status register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_B));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
printk("tvp5150: Interrupt active register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ACTIVE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_STATUS_REG_1),
- tvp5150_read(c, TVP5150_STATUS_REG_2),
- tvp5150_read(c, TVP5150_STATUS_REG_3),
- tvp5150_read(c, TVP5150_STATUS_REG_4),
- tvp5150_read(c, TVP5150_STATUS_REG_5));
+ tvp5150_read(sd, TVP5150_STATUS_REG_1),
+ tvp5150_read(sd, TVP5150_STATUS_REG_2),
+ tvp5150_read(sd, TVP5150_STATUS_REG_3),
+ tvp5150_read(sd, TVP5150_STATUS_REG_4),
+ tvp5150_read(sd, TVP5150_STATUS_REG_5));
#if 0 /* This will pop a value from vbi reg */
printk("tvp5150: VBI FIFO read data = 0x%02x\n",
- tvp5150_read(c, TVP5150_VBI_FIFO_READ_DATA));
+ tvp5150_read(sd, TVP5150_VBI_FIFO_READ_DATA));
#endif
- dump_reg_range(c,"Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
- TVP5150_TELETEXT_FIL1_END,8);
- dump_reg_range(c,"Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
- TVP5150_TELETEXT_FIL2_END,8);
+ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
+ TVP5150_TELETEXT_FIL1_END, 8);
+ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
+ TVP5150_TELETEXT_FIL2_END, 8);
printk("tvp5150: Teletext filter enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_TELETEXT_FIL_ENA));
+ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
printk("tvp5150: Interrupt status register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_A));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
printk("tvp5150: Interrupt enable register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_A));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
printk("tvp5150: Interrupt configuration = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_CONF));
+ tvp5150_read(sd, TVP5150_INT_CONF));
printk("tvp5150: VDP status register = 0x%02x\n",
- tvp5150_read(c, TVP5150_VDP_STATUS_REG));
+ tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
printk("tvp5150: FIFO word count = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_WORD_COUNT));
+ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_INT_THRESHOLD));
+ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
printk("tvp5150: FIFO reset = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_RESET));
+ tvp5150_read(sd, TVP5150_FIFO_RESET));
printk("tvp5150: Line number interrupt = 0x%02x\n",
- tvp5150_read(c, TVP5150_LINE_NUMBER_INT));
+ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_HIGH),
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_LOW));
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
printk("tvp5150: FIFO output control = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_OUT_CTRL));
+ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
printk("tvp5150: Full field enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_ENA));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
printk("tvp5150: Full field mode register = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_MODE_REG));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
- dump_reg_range(c,"CC data", TVP5150_CC_DATA_INI,
- TVP5150_CC_DATA_END,8);
+ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
+ TVP5150_CC_DATA_END, 8);
- dump_reg_range(c,"WSS data", TVP5150_WSS_DATA_INI,
- TVP5150_WSS_DATA_END,8);
+ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
+ TVP5150_WSS_DATA_END, 8);
- dump_reg_range(c,"VPS data", TVP5150_VPS_DATA_INI,
- TVP5150_VPS_DATA_END,8);
+ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
+ TVP5150_VPS_DATA_END, 8);
- dump_reg_range(c,"VITC data", TVP5150_VITC_DATA_INI,
- TVP5150_VITC_DATA_END,10);
+ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
+ TVP5150_VITC_DATA_END, 10);
- dump_reg_range(c,"Line mode", TVP5150_LINE_MODE_INI,
- TVP5150_LINE_MODE_END,8);
+ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
+ TVP5150_LINE_MODE_END, 8);
+ return 0;
}
/****************************************************************************
Basic functions
****************************************************************************/
-static inline void tvp5150_selmux(struct i2c_client *c)
+static inline void tvp5150_selmux(struct v4l2_subdev *sd)
{
int opmode=0;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
+ struct tvp5150 *decoder = to_tvp5150(sd);
int input = 0;
unsigned char val;
@@ -314,23 +311,23 @@ static inline void tvp5150_selmux(struct i2c_client *c)
break;
}
- tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i "
+ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
"=> tvp5150 input=%i, opmode=%i\n",
decoder->route.input,decoder->route.output,
input, opmode );
- tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode);
- tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input);
+ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
+ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
/* Svideo should enable YCrCb output and disable GPCL output
* For Composite and TV, it should be the reverse
*/
- val = tvp5150_read(c, TVP5150_MISC_CTL);
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
if (decoder->route.input == TVP5150_SVIDEO)
val = (val & ~0x40) | 0x10;
else
val = (val & ~0x10) | 0x40;
- tvp5150_write(c, TVP5150_MISC_CTL, val);
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
};
struct i2c_reg_value {
@@ -605,35 +602,35 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
{ (u16)-1 }
};
-static int tvp5150_write_inittab(struct i2c_client *c,
+static int tvp5150_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0xff) {
- tvp5150_write(c, regs->reg, regs->value);
+ tvp5150_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
}
-static int tvp5150_vdp_init(struct i2c_client *c,
+static int tvp5150_vdp_init(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs)
{
unsigned int i;
/* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
/* Before programming, Line mode should be at 0xff */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
/* Load Ram Table */
- while (regs->reg != (u16)-1 ) {
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8);
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg);
+ while (regs->reg != (u16)-1) {
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
- for (i=0;i<16;i++)
- tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]);
+ for (i = 0; i < 16; i++)
+ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
regs++;
}
@@ -641,11 +638,13 @@ static int tvp5150_vdp_init(struct i2c_client *c,
}
/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
-static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
+static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
struct v4l2_sliced_vbi_cap *cap)
{
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
int line;
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n");
memset(cap, 0, sizeof *cap);
while (regs->reg != (u16)-1 ) {
@@ -656,6 +655,7 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
regs++;
}
+ return 0;
}
/* Set vbi processing
@@ -671,18 +671,18 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
* LSB = field1
* MSB = field2
*/
-static int tvp5150_set_vbi(struct i2c_client *c,
+static int tvp5150_set_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs,
unsigned int type,u8 flags, int line,
const int fields)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
int pos=0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
@@ -710,174 +710,186 @@ static int tvp5150_set_vbi(struct i2c_client *c,
reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
if (fields&1) {
- tvp5150_write(c, reg, type);
+ tvp5150_write(sd, reg, type);
}
if (fields&2) {
- tvp5150_write(c, reg+1, type);
+ tvp5150_write(sd, reg+1, type);
}
return type;
}
-static int tvp5150_get_vbi(struct i2c_client *c,
+static int tvp5150_get_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs, int line)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
- int pos, type=0;
+ int pos, type = 0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
line += 3;
}
- if (line<6||line>27)
+ if (line < 6 || line > 27)
return 0;
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- pos=tvp5150_read(c, reg)&0x0f;
- if (pos<0x0f)
- type=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg) & 0x0f;
+ if (pos < 0x0f)
+ type = regs[pos].type.vbi_type;
- pos=tvp5150_read(c, reg+1)&0x0f;
- if (pos<0x0f)
- type|=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg + 1) & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
return type;
}
-#if 0
-static int decode_vbi_data(struct i2c_client *c, vbi)
-{
- count=tvp5150_read(c, TVP5150_FIFO_WORD_COUNT);
- for (i=0;i<count;i++) {
- *p=tvp5150_read(c, TVP5150_VBI_FIFO_READ_DATA);
- p++;
- }
-}
-#endif
-static int tvp5150_set_std(struct i2c_client *c, v4l2_std_id std)
+static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int fmt=0;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int fmt = 0;
- decoder->norm=std;
+ decoder->norm = std;
/* First tests should be against specific std */
if (std == V4L2_STD_ALL) {
- fmt=0; /* Autodetect mode */
+ fmt = 0; /* Autodetect mode */
} else if (std & V4L2_STD_NTSC_443) {
- fmt=0xa;
+ fmt = 0xa;
} else if (std & V4L2_STD_PAL_M) {
- fmt=0x6;
- } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) {
- fmt=0x8;
+ fmt = 0x6;
+ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ fmt = 0x8;
} else {
/* Then, test against generic ones */
- if (std & V4L2_STD_NTSC) {
- fmt=0x2;
- } else if (std & V4L2_STD_PAL) {
- fmt=0x4;
- } else if (std & V4L2_STD_SECAM) {
- fmt=0xc;
- }
+ if (std & V4L2_STD_NTSC)
+ fmt = 0x2;
+ else if (std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (std & V4L2_STD_SECAM)
+ fmt = 0xc;
}
- tvp5150_dbg(1,"Set video std register to %d.\n",fmt);
- tvp5150_write(c, TVP5150_VIDEO_STD, fmt);
-
+ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
+ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
return 0;
}
-static inline void tvp5150_reset(struct i2c_client *c)
+static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (decoder->norm == std)
+ return 0;
+
+ return tvp5150_set_std(sd, std);
+}
+
+static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
u8 msb_id, lsb_id, msb_rom, lsb_rom;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID);
- lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID);
- msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER);
- lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER);
+ msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
+ lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
+ msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
+ lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- if ((msb_rom==4)&&(lsb_rom==0)) { /* Is TVP5150AM1 */
- tvp5150_info("tvp%02x%02xam1 detected.\n",msb_id, lsb_id);
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
/* ITU-T BT.656.4 timing */
- tvp5150_write(c,TVP5150_REV_SELECT,0);
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
} else {
- if ((msb_rom==3)||(lsb_rom==0x21)) { /* Is TVP5150A */
- tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id);
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
} else {
- tvp5150_info("*** unknown tvp%02x%02x chip detected.\n",msb_id,lsb_id);
- tvp5150_info("*** Rom ver is %d.%d\n",msb_rom,lsb_rom);
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
}
}
/* Initializes TVP5150 to its default values */
- tvp5150_write_inittab(c, tvp5150_init_default);
+ tvp5150_write_inittab(sd, tvp5150_init_default);
/* Initializes VDP registers */
- tvp5150_vdp_init(c, vbi_ram_default);
+ tvp5150_vdp_init(sd, vbi_ram_default);
/* Selects decoder input */
- tvp5150_selmux(c);
+ tvp5150_selmux(sd);
/* Initializes TVP5150 to stream enabled values */
- tvp5150_write_inittab(c, tvp5150_init_enable);
+ tvp5150_write_inittab(sd, tvp5150_init_enable);
/* Initialize image preferences */
- tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright);
- tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_HUE_CTL, decoder->hue);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue);
- tvp5150_set_std(c, decoder->norm);
+ tvp5150_set_std(sd, decoder->norm);
+ return 0;
};
-static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n");
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = tvp5150_read(c, TVP5150_BRIGHT_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL);
return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = tvp5150_read(c, TVP5150_CONTRAST_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL);
return 0;
case V4L2_CID_SATURATION:
- ctrl->value = tvp5150_read(c, TVP5150_SATURATION_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL);
return 0;
case V4L2_CID_HUE:
- ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL);
return 0;
}
return -EINVAL;
}
-static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ u8 i, n;
+ n = ARRAY_SIZE(tvp5150_qctrl);
+
+ for (i = 0; i < n; i++) {
+ if (ctrl->id != tvp5150_qctrl[i].id)
+ continue;
+ if (ctrl->value < tvp5150_qctrl[i].minimum ||
+ ctrl->value > tvp5150_qctrl[i].maximum)
+ return -ERANGE;
+ v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- tvp5150_write(c, TVP5150_BRIGHT_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value);
return 0;
case V4L2_CID_CONTRAST:
- tvp5150_write(c, TVP5150_CONTRAST_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value);
return 0;
case V4L2_CID_SATURATION:
- tvp5150_write(c, TVP5150_SATURATION_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value);
return 0;
case V4L2_CID_HUE:
- tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value);
return 0;
}
return -EINVAL;
@@ -886,289 +898,265 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
/****************************************************************************
I2C Command
****************************************************************************/
-static int tvp5150_command(struct i2c_client *c,
- unsigned int cmd, void *arg)
-{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
-
- switch (cmd) {
-
- case 0:
- case VIDIOC_INT_RESET:
- tvp5150_reset(c);
- break;
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
-
- *route = decoder->route;
- break;
- }
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
- decoder->route = *route;
- tvp5150_selmux(c);
- break;
- }
- case VIDIOC_S_STD:
- if (decoder->norm == *(v4l2_std_id *)arg)
- break;
- return tvp5150_set_std(c, *(v4l2_std_id *)arg);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = decoder->norm;
- break;
+static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
- case VIDIOC_G_SLICED_VBI_CAP:
- {
- struct v4l2_sliced_vbi_cap *cap = arg;
- tvp5150_dbg(1, "VIDIOC_G_SLICED_VBI_CAP\n");
+ decoder->route = *route;
+ tvp5150_selmux(sd);
+ return 0;
+}
- tvp5150_vbi_get_cap(vbi_ram_default, cap);
- break;
+static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i;
+
+ /* raw vbi */
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* this is for capturing 36 raw vbi lines
+ if there's a way to cut off the beginning 2 vbi lines
+ with the tvp5150 then the vbi line count could be lowered
+ to 17 lines/field again, although I couldn't find a register
+ which could do that cropping */
+ if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY)
+ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+ if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) {
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+ }
+ return 0;
}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
- int i;
-
- fmt = arg;
- /* raw vbi */
- if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- /* this is for capturing 36 raw vbi lines
- if there's a way to cut off the beginning 2 vbi lines
- with the tvp5150 then the vbi line count could be lowered
- to 17 lines/field again, although I couldn't find a register
- which could do that cropping */
- if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY)
- tvp5150_write(c, TVP5150_LUMA_PROC_CTL_1, 0x70);
- if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) {
- tvp5150_write(c, TVP5150_VERT_BLANKING_START, 0x00);
- tvp5150_write(c, TVP5150_VERT_BLANKING_STOP, 0x01);
- }
- break;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (svbi->service_set != 0) {
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[1][i] = 0;
+ svbi->service_lines[0][i] =
+ tvp5150_set_vbi(sd, vbi_ram_default,
+ svbi->service_lines[0][i], 0xf0, i, 3);
}
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- if (svbi->service_set != 0) {
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[1][i] = 0;
-
- svbi->service_lines[0][i]=tvp5150_set_vbi(c,
- vbi_ram_default,
- svbi->service_lines[0][i],0xf0,i,3);
- }
- /* Enables FIFO */
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,1);
- } else {
- /* Disables FIFO*/
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,0);
+ /* Enables FIFO */
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+ } else {
+ /* Disables FIFO*/
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
- /* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
- /* Disable Line modes */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
- }
- break;
+ /* Disable Line modes */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
}
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
+ return 0;
+}
- int i, mask=0;
+static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i, mask = 0;
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- memset(svbi, 0, sizeof(*svbi));
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[0][i]=tvp5150_get_vbi(c,
- vbi_ram_default,i);
- mask|=svbi->service_lines[0][i];
- }
- svbi->service_set=mask;
- break;
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[0][i] =
+ tvp5150_get_vbi(sd, vbi_ram_default, i);
+ mask |= svbi->service_lines[0][i];
}
+ svbi->service_set = mask;
+ return 0;
+}
+
#if 0
- /* This will not work for USB devices */
- case VIDIOC_INT_G_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
- u8 status;
-
- status=tvp5150_read(c, TVP5150_VDP_STATUS_REG);
-
- if (data->id & V4L2_SLICED_CAPTION) {
- if (!field && (status&0x10)) {
- data->data[0]=tvp5150_read(c, TVP5150_CC_DATA_INI);
- data->data[1]=tvp5150_read(c, TVP5150_CC_DATA_INI+1);
- } if (field && (status&0x8)) {
- data->data[0]=tvp5150_read(c, TVP5150_CC_DATA_INI+2);
- data->data[1]=tvp5150_read(c, TVP5150_CC_DATA_INI+3);
- } else data->id=0;
- return 0;
- } else if (data->id & V4L2_SLICED_WSS) {
- } else if (data->id & V4L2_SLICED_VPS) {
+/* This will not work for USB devices */
+static int tvp5150_g_vbi_data(struct v4l2_subdev *sd,
+ struct v4l2_sliced_vbi_data *data)
+{
+ u8 status = tvp5150_read(sd, TVP5150_VDP_STATUS_REG);
+
+ if (data->id & V4L2_SLICED_CAPTION) {
+ if (!field && (status & 0x10)) {
+ data->data[0] = tvp5150_read(sd, TVP5150_CC_DATA_INI);
+ data->data[1] = tvp5150_read(sd, TVP5150_CC_DATA_INI+1);
+ } if (field && (status & 0x8)) {
+ data->data[0] = tvp5150_read(sd, TVP5150_CC_DATA_INI+2);
+ data->data[1] = tvp5150_read(sd, TVP5150_CC_DATA_INI+3);
+ } else {
+ data->id = 0;
}
- break;
+ return 0;
+ } else if (data->id & V4L2_SLICED_WSS) {
+ } else if (data->id & V4L2_SLICED_VPS) {
}
- case VIDIOC_INT_DECODE_VBI_LINE:
- {
- struct v4l2_decode_vbi_line *vbi = arg;
- u8 status;
+ return 0;
+}
- status=tvp5150_read(c, TVP5150_VDP_STATUS_REG);
+static int decode_vbi_data(struct v4l2_subdev *sd, vbi)
+{
+ count = tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT);
- if (status&0x80) {
- tvp5150_err("Full field error");
- status&=0x7f;
- tvp5150_write(c, TVP5150_VDP_STATUS_REG,status);
- }
+ for (i = 0; i < count; i++) {
+ *p = tvp5150_read(sd, TVP5150_VBI_FIFO_READ_DATA);
+ p++;
+ }
+}
- /* FIFO */
- /* Current V4L2 API allows sliced VBI only with fifo mode,
- since line and types are not provided on other means
- on tvp5150.
- */
- if (!(status&0x40)) /* Has FIFO data */
- decode_vbi_data(c,vbi);
+static int tvp5150_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi)
+{
+ u8 status = tvp5150_read(sd, TVP5150_VDP_STATUS_REG);
- break;
+ if (status & 0x80) {
+ v4l2_err(sd, "Full field error");
+ status &= 0x7f;
+ tvp5150_write(sd, TVP5150_VDP_STATUS_REG, status);
}
-#endif
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = tvp5150_read(c, reg->reg & 0xff);
- else
- tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ /* FIFO */
+ /* Current V4L2 API allows sliced VBI only with fifo mode,
+ since line and types are not provided on other means
+ on tvp5150.
+ */
+ if (!(status & 0x40)) /* Has FIFO data */
+ decode_vbi_data(sd, vbi);
+ return 0;
+}
#endif
- case VIDIOC_LOG_STATUS:
- dump_reg(c);
- break;
+static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status = tvp5150_read(c, 0x88);
+ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
+ rev);
+}
- tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n");
- for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
- if (qc->id && qc->id == tvp5150_qctrl[i].id) {
- memcpy(qc, &(tvp5150_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- tvp5150_dbg(1, "VIDIOC_G_CTRL called\n");
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = tvp5150_read(sd, reg->reg & 0xff);
+ return 0;
+}
- return tvp5150_get_ctrl(c, ctrl);
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- u8 i, n;
- n = ARRAY_SIZE(tvp5150_qctrl);
- for (i = 0; i < n; i++)
- if (ctrl->id == tvp5150_qctrl[i].id) {
- if (ctrl->value <
- tvp5150_qctrl[i].minimum
- || ctrl->value >
- tvp5150_qctrl[i].maximum)
- return -ERANGE;
- tvp5150_dbg(1,
- "VIDIOC_S_CTRL: id=%d, value=%d\n",
- ctrl->id, ctrl->value);
- return tvp5150_set_ctrl(c, ctrl);
- }
- return -EINVAL;
- }
+static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- default:
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int status = tvp5150_read(sd, 0x88);
+
+ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
return 0;
}
+static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n");
+
+ for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
+ if (qc->id && qc->id == tvp5150_qctrl[i].id) {
+ memcpy(qc, &(tvp5150_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
+ .log_status = tvp5150_log_status,
+ .g_ctrl = tvp5150_g_ctrl,
+ .s_ctrl = tvp5150_s_ctrl,
+ .queryctrl = tvp5150_queryctrl,
+ .reset = tvp5150_reset,
+ .g_chip_ident = tvp5150_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp5150_g_register,
+ .s_register = tvp5150_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
+ .s_std = tvp5150_s_std,
+ .g_tuner = tvp5150_g_tuner,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_routing = tvp5150_s_routing,
+ .g_fmt = tvp5150_g_fmt,
+ .s_fmt = tvp5150_s_fmt,
+ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
+};
+
+static const struct v4l2_subdev_ops tvp5150_ops = {
+ .core = &tvp5150_core_ops,
+ .tuner = &tvp5150_tuner_ops,
+ .video = &tvp5150_video_ops,
+};
+
+
/****************************************************************************
I2C Client & Driver
****************************************************************************/
-static struct i2c_driver driver;
-static struct i2c_client client_template = {
- .name = "(unset)",
- .driver = &driver,
-};
-
-static int tvp5150_detect_client(struct i2c_adapter *adapter,
- int address, int kind)
+static int tvp5150_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
{
- struct i2c_client *c;
struct tvp5150 *core;
- int rv;
-
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: detecting tvp5150 client on address 0x%x\n",
- address << 1);
-
- client_template.adapter = adapter;
- client_template.addr = address;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality
- (adapter,
+ if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
-
- c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!c)
- return -ENOMEM;
- memcpy(c, &client_template, sizeof(struct i2c_client));
+ return -EIO;
core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
if (!core) {
- kfree(c);
return -ENOMEM;
}
- i2c_set_clientdata(c, core);
-
- rv = i2c_attach_client(c);
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
core->norm = V4L2_STD_ALL; /* Default is autodetect */
core->route.input = TVP5150_COMPOSITE1;
@@ -1178,75 +1166,42 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter,
core->hue = 0;
core->sat = 128;
- if (rv) {
- kfree(c);
- kfree(core);
- return rv;
- }
-
if (debug > 1)
- dump_reg(c);
+ tvp5150_log_status(sd);
return 0;
}
-static int tvp5150_attach_adapter(struct i2c_adapter *adapter)
-{
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: starting probe for adapter %s (0x%x)\n",
- adapter->name, adapter->id);
- return i2c_probe(adapter, &addr_data, &tvp5150_detect_client);
-}
-
-static int tvp5150_detach_client(struct i2c_client *c)
+static int tvp5150_remove(struct i2c_client *c)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int err;
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
- tvp5150_dbg(1,
+ v4l2_dbg(1, debug, sd,
"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
c->addr << 1);
- err = i2c_detach_client(c);
- if (err) {
- return err;
- }
-
- kfree(decoder);
- kfree(c);
-
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_tvp5150(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tvp5150",
- },
- .id = I2C_DRIVERID_TVP5150,
-
- .attach_adapter = tvp5150_attach_adapter,
- .detach_client = tvp5150_detach_client,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+static const struct i2c_device_id tvp5150_id[] = {
+ { "tvp5150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+#endif
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tvp5150",
+ .driverid = I2C_DRIVERID_TVP5150,
.command = tvp5150_command,
-#if 0
- .driver = {
- .suspend = tvp5150_suspend,
- .resume = tvp5150_resume,
- },
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+ .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ .id_table = tvp5150_id,
#endif
};
-
-static int __init tvp5150_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit tvp5150_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(tvp5150_init);
-module_exit(tvp5150_exit);
diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c
index 332929bb2..85409c624 100644
--- a/linux/drivers/media/video/usbvideo/usbvideo.c
+++ b/linux/drivers/media/video/usbvideo/usbvideo.c
@@ -1123,7 +1123,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
if (uvd->debug > 1)
dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
- if (0 < usbvideo_ClientIncModCount(uvd))
+ if (usbvideo_ClientIncModCount(uvd) < 0)
return -ENODEV;
mutex_lock(&uvd->lock);
diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c
index 91849084f..0b832f210 100644
--- a/linux/drivers/media/video/v4l2-dev.c
+++ b/linux/drivers/media/video/v4l2-dev.c
@@ -30,6 +30,7 @@
#include <asm/system.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include "compat.h"
#define VIDEO_NUM_DEVICES 256
@@ -43,17 +44,17 @@
static ssize_t show_index(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%i\n", vfd->index);
+ return sprintf(buf, "%i\n", vdev->index);
}
static ssize_t show_name(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}
static struct device_attribute video_device_attrs[] = {
@@ -64,16 +65,16 @@ static struct device_attribute video_device_attrs[] = {
#else
static ssize_t show_index(struct class_device *cd, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%i\n", vfd->index);
+ return sprintf(buf, "%i\n", vdev->index);
}
static ssize_t show_name(struct class_device *cd, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}
static CLASS_DEVICE_ATTR(index, S_IRUGO, show_index, NULL);
@@ -93,71 +94,77 @@ struct video_device *video_device_alloc(void)
}
EXPORT_SYMBOL(video_device_alloc);
-void video_device_release(struct video_device *vfd)
+void video_device_release(struct video_device *vdev)
{
- kfree(vfd);
+ kfree(vdev);
}
EXPORT_SYMBOL(video_device_release);
-void video_device_release_empty(struct video_device *vfd)
+void video_device_release_empty(struct video_device *vdev)
{
/* Do nothing */
/* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL(video_device_release_empty);
-/* Called when the last user of the character device is gone. */
-static void v4l2_chardev_release(struct kobject *kobj)
+static inline void video_get(struct video_device *vdev)
{
- struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ class_device_get(&vdev->dev);
+#else
+ get_device(&vdev->dev);
+#endif
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ class_device_put(&vdev->dev);
+#else
+ put_device(&vdev->dev);
+#endif
+}
+
+/* Called when the last user of the video device exits. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void v4l2_device_release(struct class_device *cd)
+#else
+static void v4l2_device_release(struct device *cd)
+#endif
+{
+ struct video_device *vdev = to_video_device(cd);
mutex_lock(&videodev_lock);
- if (video_device[vfd->minor] != vfd) {
+ if (video_device[vdev->minor] != vdev) {
mutex_unlock(&videodev_lock);
- BUG();
+ /* should not happen */
+ WARN_ON(1);
return;
}
/* Free up this device for reuse */
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[vfd->vfl_type]);
- mutex_unlock(&videodev_lock);
+ video_device[vdev->minor] = NULL;
- /* Release the character device */
- vfd->cdev_release(kobj);
- /* Release video_device and perform other
- cleanups as needed. */
- if (vfd->release)
- vfd->release(vfd);
-}
+ /* Delete the cdev on this minor as well */
+ cdev_del(vdev->cdev);
+ /* Just in case some driver tries to access this from
+ the release() callback. */
+ vdev->cdev = NULL;
-/* The new kobj_type for the character device */
-static struct kobj_type v4l2_ktype_cdev_default = {
- .release = v4l2_chardev_release,
-};
+ /* Mark minor as free */
+ clear_bit(vdev->num, video_nums[vdev->vfl_type]);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void video_release(struct class_device *cd)
-#else
-static void video_release(struct device *cd)
-#endif
-{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ mutex_unlock(&videodev_lock);
- /* It's now safe to delete the char device.
- This will either trigger the v4l2_chardev_release immediately (if
- the refcount goes to 0) or later when the last user of the
- character device closes it. */
- cdev_del(&vfd->cdev);
+ /* Release video_device and perform other
+ cleanups as needed. */
+ vdev->release(vdev);
}
static struct class video_class = {
.name = VIDEO_NAME,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- .release = video_release,
-#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
.dev_attrs = video_device_attrs,
- .dev_release = video_release,
#endif
};
@@ -171,13 +178,163 @@ struct video_device *video_devdata(struct file *file)
}
EXPORT_SYMBOL(video_devdata);
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->read)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->read(filp, buf, sz, off);
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->write)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->write(filp, buf, sz, off);
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->poll || video_is_unregistered(vdev))
+ return DEFAULT_POLLMASK;
+ return vdev->fops->poll(filp, poll);
+}
+
+static int v4l2_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->ioctl(inode, filp, cmd, arg);
+}
+
+static long v4l2_unlocked_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->unlocked_ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long v4l2_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->compat_ioctl)
+ return -ENOIOCTLCMD;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->compat_ioctl(filp, cmd, arg);
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->mmap ||
+ video_is_unregistered(vdev))
+ return -ENODEV;
+ return vdev->fops->mmap(filp, vm);
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev;
+ int ret;
+
+ /* Check if the video device is available */
+ mutex_lock(&videodev_lock);
+ vdev = video_devdata(filp);
+ /* return ENODEV if the video device has been removed
+ already or if it is not registered anymore. */
+ if (vdev == NULL || video_is_unregistered(vdev)) {
+ mutex_unlock(&videodev_lock);
+ return -ENODEV;
+ }
+ /* and increase the device refcount */
+ video_get(vdev);
+ mutex_unlock(&videodev_lock);
+ ret = vdev->fops->open(inode, filp);
+ /* decrease the refcount in case of an error */
+ if (ret)
+ video_put(vdev);
+ return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = vdev->fops->release(inode, filp);
+
+ /* decrease the refcount unconditionally since the release()
+ return value is ignored. */
+ video_put(vdev);
+ return ret;
+}
+
+static const struct file_operations v4l2_unlocked_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
+static const struct file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
/**
* get_index - assign stream number based on parent device
- * @vdev: video_device to assign index number to, vdev->dev should be assigned
- * @num: -1 if auto assign, requested number otherwise
+ * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ * @num: -1 if auto assign, requested number otherwise
*
+ * Note that when this is called the new device has not yet been registered
+ * in the video_device array.
*
- * returns -ENFILE if num is already in use, a free index number if
+ * Returns -ENFILE if num is already in use, a free index number if
* successful.
*/
static int get_index(struct video_device *vdev, int num)
@@ -200,7 +357,6 @@ static int get_index(struct video_device *vdev, int num)
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_device[i] != NULL &&
- video_device[i] != vdev &&
video_device[i]->parent == vdev->parent) {
used |= 1 << video_device[i]->index;
}
@@ -216,17 +372,15 @@ static int get_index(struct video_device *vdev, int num)
return i > max_index ? -ENFILE : i;
}
-static const struct file_operations video_fops;
-
-int video_register_device(struct video_device *vfd, int type, int nr)
+int video_register_device(struct video_device *vdev, int type, int nr)
{
- return video_register_device_index(vfd, type, nr, -1);
+ return video_register_device_index(vdev, type, nr, -1);
}
EXPORT_SYMBOL(video_register_device);
/**
* video_register_device_index - register video4linux devices
- * @vfd: video device structure we want to register
+ * @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
@@ -250,8 +404,7 @@ EXPORT_SYMBOL(video_register_device);
*
* %VFL_TYPE_RADIO - A radio card
*/
-
-int video_register_device_index(struct video_device *vfd, int type, int nr,
+int video_register_device_index(struct video_device *vdev, int type, int nr,
int index)
{
int i = 0;
@@ -259,14 +412,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
- void *priv = video_get_drvdata(vfd);
+ void *priv = video_get_drvdata(vdev);
- /* the release callback MUST be present */
- BUG_ON(!vfd->release);
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+ if (vdev)
+ vdev->minor = -1;
- if (vfd == NULL)
+ /* the release callback MUST be present */
+ WARN_ON(!vdev || !vdev->release);
+ if (!vdev || !vdev->release)
return -EINVAL;
+ /* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
@@ -286,8 +444,12 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -EINVAL;
}
- vfd->vfl_type = type;
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+ if (vdev->v4l2_dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ /* Part 2: find a free minor, kernel number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
@@ -318,14 +480,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
}
#endif
- /* Initialize the character device */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
- cdev_init(&vfd->cdev, vfd->fops);
-#else
- cdev_init(&vfd->cdev, (struct file_operations *)vfd->fops);
-#endif
- vfd->cdev.owner = vfd->fops->owner;
- /* pick a minor number */
+ /* Pick a minor number */
mutex_lock(&videodev_lock);
nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
if (nr == minor_cnt)
@@ -349,98 +504,125 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -ENFILE;
}
#endif
- vfd->minor = i + minor_offset;
- vfd->num = nr;
+ vdev->minor = i + minor_offset;
+ vdev->num = nr;
set_bit(nr, video_nums[type]);
- BUG_ON(video_device[vfd->minor]);
- video_device[vfd->minor] = vfd;
-
- ret = get_index(vfd, index);
- vfd->index = ret;
-
+ /* Should not happen since we thought this minor was free */
+ WARN_ON(video_device[vdev->minor] != NULL);
+ ret = vdev->index = get_index(vdev, index);
mutex_unlock(&videodev_lock);
if (ret < 0) {
printk(KERN_ERR "%s: get_index failed\n", __func__);
- goto fail_minor;
+ goto cleanup;
}
- ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
+ /* Part 3: Initialize the character device */
+ vdev->cdev = cdev_alloc();
+ if (vdev->cdev == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
+ if (vdev->fops->unlocked_ioctl)
+ vdev->cdev->ops = &v4l2_unlocked_fops;
+ else
+ vdev->cdev->ops = &v4l2_fops;
+#else
+ if (vdev->fops->unlocked_ioctl)
+ vdev->cdev->ops = (struct file_operations *)&v4l2_unlocked_fops;
+ else
+ vdev->cdev->ops = (struct file_operations *)&v4l2_fops;
+#endif
+ vdev->cdev->owner = vdev->fops->owner;
+ ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
- goto fail_minor;
+ kfree(vdev->cdev);
+ vdev->cdev = NULL;
+ goto cleanup;
}
- /* sysfs class */
- memset(&vfd->dev, 0, sizeof(vfd->dev));
+
+ /* Part 4: register the device with sysfs */
+ memset(&vdev->dev, 0, sizeof(vdev->dev));
/* The memset above cleared the device's drvdata, so
put back the copy we made earlier. */
- video_set_drvdata(vfd, priv);
- vfd->dev.class = &video_class;
- vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
+ video_set_drvdata(vdev, priv);
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- if (vfd->parent)
- vfd->dev.dev = vfd->parent;
- sprintf(vfd->dev.class_id, "%s%d", name_base, nr);
- ret = class_device_register(&vfd->dev);
+ if (vdev->parent)
+ vdev->dev.dev = vdev->parent;
+ sprintf(vdev->dev.class_id, "%s%d", name_base, nr);
+ ret = class_device_register(&vdev->dev);
#else
- if (vfd->parent)
- vfd->dev.parent = vfd->parent;
- dev_set_name(&vfd->dev, "%s%d", name_base, nr);
- ret = device_register(&vfd->dev);
+ if (vdev->parent)
+ vdev->dev.parent = vdev->parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, nr);
+ ret = device_register(&vdev->dev);
#endif
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto del_cdev;
+ goto cleanup;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- ret = class_device_create_file(&vfd->dev, &class_device_attr_name);
+ ret = class_device_create_file(&vdev->dev, &class_device_attr_name);
if (ret < 0) {
printk(KERN_ERR "%s: class_device_create_file 'name' failed\n",
__func__);
- class_device_unregister(&vfd->dev);
- goto del_cdev;
+ class_device_unregister(&vdev->dev);
+ goto cleanup;
}
- ret = class_device_create_file(&vfd->dev, &class_device_attr_index);
+ ret = class_device_create_file(&vdev->dev, &class_device_attr_index);
if (ret < 0) {
printk(KERN_ERR "%s: class_device_create_file 'index' failed\n",
__func__);
- class_device_unregister(&vfd->dev);
- goto del_cdev;
+ class_device_unregister(&vdev->dev);
+ goto cleanup;
}
#endif
- /* Remember the cdev's release function */
- vfd->cdev_release = vfd->cdev.kobj.ktype->release;
- /* Install our own */
- vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
- return 0;
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
-del_cdev:
- cdev_del(&vfd->cdev);
+ /* Part 5: Activate this minor. The char device can now be used. */
+ mutex_lock(&videodev_lock);
+ video_device[vdev->minor] = vdev;
+ mutex_unlock(&videodev_lock);
+ return 0;
-fail_minor:
+cleanup:
mutex_lock(&videodev_lock);
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[type]);
+ if (vdev->cdev)
+ cdev_del(vdev->cdev);
+ clear_bit(vdev->num, video_nums[type]);
mutex_unlock(&videodev_lock);
- vfd->minor = -1;
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(video_register_device_index);
/**
* video_unregister_device - unregister a video4linux device
- * @vfd: the device to unregister
+ * @vdev: the device to unregister
*
- * This unregisters the passed device and deassigns the minor
- * number. Future open calls will be met with errors.
+ * This unregisters the passed device. Future open calls will
+ * be met with errors.
*/
-
-void video_unregister_device(struct video_device *vfd)
+void video_unregister_device(struct video_device *vdev)
{
+ /* Check if vdev was ever registered at all */
+ if (!vdev || vdev->minor < 0)
+ return;
+
+ mutex_lock(&videodev_lock);
+ set_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- class_device_unregister(&vfd->dev);
+ class_device_unregister(&vdev->dev);
#else
- device_unregister(&vfd->dev);
+ device_unregister(&vdev->dev);
#endif
}
EXPORT_SYMBOL(video_unregister_device);
diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c
index fe1f01c97..e3612f29d 100644
--- a/linux/drivers/media/video/v4l2-subdev.c
+++ b/linux/drivers/media/video/v4l2-subdev.c
@@ -40,13 +40,13 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
case VIDIOC_G_CHIP_IDENT:
return v4l2_subdev_call(sd, core, g_chip_ident, arg);
case VIDIOC_INT_S_STANDBY:
- return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg);
+ return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0);
case VIDIOC_INT_RESET:
- return v4l2_subdev_call(sd, core, reset, *(u32 *)arg);
+ return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0);
case VIDIOC_INT_S_GPIO:
- return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg);
+ return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0);
case VIDIOC_INT_INIT:
- return v4l2_subdev_call(sd, core, init, *(u32 *)arg);
+ return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0);
#ifdef CONFIG_VIDEO_ADV_DEBUG
case VIDIOC_DBG_G_REGISTER:
return v4l2_subdev_call(sd, core, g_register, arg);
@@ -90,6 +90,8 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
return v4l2_subdev_call(sd, video, s_vbi_data, arg);
case VIDIOC_INT_G_VBI_DATA:
return v4l2_subdev_call(sd, video, g_vbi_data, arg);
+ case VIDIOC_G_SLICED_VBI_CAP:
+ return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
case VIDIOC_S_FMT:
return v4l2_subdev_call(sd, video, s_fmt, arg);
case VIDIOC_G_FMT:
diff --git a/linux/include/media/v4l2-chip-ident.h b/linux/include/media/v4l2-chip-ident.h
index 173e9af5f..43dbb659f 100644
--- a/linux/include/media/v4l2-chip-ident.h
+++ b/linux/include/media/v4l2-chip-ident.h
@@ -71,6 +71,9 @@ enum {
/* module vp27smpx: just ident 2700 */
V4L2_IDENT_VP27SMPX = 2700,
+ /* module tvp5150 */
+ V4L2_IDENT_TVP5150 = 5150,
+
/* module cs5345: just ident 5345 */
V4L2_IDENT_CS5345 = 5345,
diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h
index bd59577d7..c6cf5b1b2 100644
--- a/linux/include/media/v4l2-dev.h
+++ b/linux/include/media/v4l2-dev.h
@@ -25,6 +25,12 @@
#define VFL_TYPE_MAX 4
struct v4l2_ioctl_callbacks;
+struct v4l2_device;
+
+/* Flag to mark the video_device struct as unregistered.
+ Drivers can set this flag if they want to block all future
+ device access. It is set by video_unregister_device. */
+#define V4L2_FL_UNREGISTERED (0)
/*
* Newer version of video_device, handled by videodev2.c
@@ -43,15 +49,20 @@ struct video_device
#else
struct class_device dev;
#endif
- struct cdev cdev; /* character device */
- void (*cdev_release)(struct kobject *kobj);
+ struct cdev *cdev; /* character device */
+
+ /* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
+ struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* device info */
char name[32];
int vfl_type;
+ /* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
+ /* use bitops to set/clear/test flags */
+ unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index;
@@ -62,7 +73,7 @@ struct video_device
v4l2_std_id current_norm; /* Current tvnorm */
/* callbacks */
- void (*release)(struct video_device *vfd);
+ void (*release)(struct video_device *vdev);
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
@@ -71,43 +82,48 @@ struct video_device
/* dev to video-device */
#define to_video_device(cd) container_of(cd, struct video_device, dev)
-/* Register and unregister devices. Note that if video_register_device fails,
+/* Register video devices. Note that if video_register_device fails,
the release() callback of the video_device structure is *not* called, so
the caller is responsible for freeing any data. Usually that means that
- you call video_device_release() on failure. */
-int __must_check video_register_device(struct video_device *vfd, int type, int nr);
-int __must_check video_register_device_index(struct video_device *vfd,
+ you call video_device_release() on failure.
+
+ Also note that vdev->minor is set to -1 if the registration failed. */
+int __must_check video_register_device(struct video_device *vdev, int type, int nr);
+int __must_check video_register_device_index(struct video_device *vdev,
int type, int nr, int index);
-void video_unregister_device(struct video_device *vfd);
+
+/* Unregister video devices. Will do nothing if vdev == NULL or
+ vdev->minor < 0. */
+void video_unregister_device(struct video_device *vdev);
/* helper functions to alloc/release struct video_device, the
latter can also be used for video_device->release(). */
struct video_device * __must_check video_device_alloc(void);
-/* this release function frees the vfd pointer */
-void video_device_release(struct video_device *vfd);
+/* this release function frees the vdev pointer */
+void video_device_release(struct video_device *vdev);
/* this release function does nothing, use when the video_device is a
static global struct. Note that having a static video_device is
a dubious construction at best. */
-void video_device_release_empty(struct video_device *vfd);
+void video_device_release_empty(struct video_device *vdev);
/* helper functions to access driver private data. */
-static inline void *video_get_drvdata(struct video_device *dev)
+static inline void *video_get_drvdata(struct video_device *vdev)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- return class_get_devdata(&dev->dev);
+ return class_get_devdata(&vdev->dev);
#else
- return dev_get_drvdata(&dev->dev);
+ return dev_get_drvdata(&vdev->dev);
#endif
}
-static inline void video_set_drvdata(struct video_device *dev, void *data)
+static inline void video_set_drvdata(struct video_device *vdev, void *data)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
- class_set_devdata(&dev->dev, data);
+ class_set_devdata(&vdev->dev, data);
#else
- dev_set_drvdata(&dev->dev, data);
+ dev_set_drvdata(&vdev->dev, data);
#endif
}
@@ -120,4 +136,9 @@ static inline void *video_drvdata(struct file *file)
return video_get_drvdata(video_devdata(file));
}
+static inline int video_is_unregistered(struct video_device *vdev)
+{
+ return test_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+}
+
#endif /* _V4L2_DEV_H */
diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h
index bc9e0fbf2..ceef016bb 100644
--- a/linux/include/media/v4l2-subdev.h
+++ b/linux/include/media/v4l2-subdev.h
@@ -79,7 +79,7 @@ struct v4l2_subdev_core_ops {
int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
- int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg);
+ int (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
#ifdef CONFIG_VIDEO_ADV_DEBUG
int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg);
int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg);
@@ -110,6 +110,7 @@ struct v4l2_subdev_video_ops {
int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);
int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);
int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
+ int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
int (*s_stream)(struct v4l2_subdev *sd, int enable);
int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);